T 1 T_1 T1——gold(3079)
Description:
有一个
n
⋅
m
n \cdot m
n⋅m的矩形,对于每一行,可以选择前
k
,
k
∈
[
1
,
n
]
k,k\in[1,n]
k,k∈[1,n]个数。
求全部选完后的平均值的最大值。
n
⋅
m
≤
1
0
5
,
A
i
,
j
≤
1
0
9
n \cdot m\le 10^5,A_{i,j}\le 10^9
n⋅m≤105,Ai,j≤109
Solution:
- 一看到求平均数、中位数之类的,有极大可能是要二分答案的。
- 对于check,我们统计出每一行的前 k k k个数与 m i d mid mid的差值和的最大值的和,即比较当前mid是否最优。
- 这样复杂度为 Θ ( n m log ( 1 0 9 ) ) \Theta(nm\log(10^9)) Θ(nmlog(109))
Code:
#include<bits/stdc++.h>
using namespace std;
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
#define db double
const db inf=1.0/0.0;
const int N=1e5+2;
int n,m;
vector<int>A[N];
bool check(db x){
db res=0;
SREP(i,0,n){
db mx=-inf,sum=0;
SREP(j,0,m){
sum+=A[i][j]-x;
chkmax(mx,sum);
}
res+=mx;
}
return res>=0;
}
int main(){
// freopen("gold.in","r",stdin);
// freopen("gold.out","w",stdout);
scanf("%d%d",&n,&m);
SREP(i,0,n){
A[i].resize(m);
SREP(j,0,m) scanf("%d",&A[i][j]);
}
db l=0,r=1e9;
while(r-l>1e-6){
db mid=1.0*(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.4lf\n",l);
return 0;
}
T 2 T_2 T2——road(3080)
Description:
有两排城市,每一排的城市编号都为1~n,对于每对编号相同的城市建一条边。
求最大的城市集合,满足集合内城市之间的边都相交。
n
≤
1
0
5
n\le 10^5
n≤105
Solution:
- 通过模拟小数据,将第二排的城市编号翻转,发现,符合的城市集合即为两排城市的公共子序列。
- 那么问题就是求最长公共子序列了。
- 首先有一个 Θ ( n 2 ) \Theta(n^2) Θ(n2)求最长公共子序列的 d p dp dp,但复杂度显然不够。
- 所以就需要数据结构优化一下,达到 Θ ( n log n ) \Theta(n\log n) Θ(nlogn),这里线段树或者树状数组都行。
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
const int N=100005;
int n;
int A[N],B[N],T[N];
int bit[N];
void add(int x,int v){
for(;x;x-=(x&-x)) chkmax(bit[x],v);
}
int query(int x){
int res=0;
for(;x<N;x+=(x&-x)) chkmax(res,bit[x]);
return res;
}
int main(){
// freopen("road.in","r",stdin);
// freopen("road.out","w",stdout);
scanf("%d",&n);
REP(i,1,n) Rd(A[i]);
REP(i,1,n) Rd(B[i]),T[B[i]]=i;
int ans=0;
REP(i,1,n){
int res=query(T[A[i]]);
chkmax(ans,res+1);
add(T[A[i]],res+1);
}
printf("%d\n",ans);
return 0;
}
T 3 T_3 T3——queue(3081)
Description:
有一棵树,现在有
m
m
m个操作,对于每个操作,有
2
2
2种:
1.有
x
x
x个人从根
1
1
1往下走(走的策略是该节点的编号最小且没有人),求最后一人在哪个节点。
2.将节点
x
x
x的人带走,
x
x
x以上的节点的人会往下补充,求有多少人移动了。
n
,
m
≤
1
0
5
n,m\le10^5
n,m≤105
Solution:
- 首先对于一棵树,我们可以先将其转化到序列上,即 d f s dfs dfs序。
- 并且,我们先将边的编号排序,这样保证最小。
- 对于操作1,即为从后往前覆盖序列;
- 对于操作2,即为修改一条链上的信息,以及求出其标记的链的长度。
- 对于序列的覆盖以及修改,很容易想到线段树,而对于求标记的链长度,我们可以倍增跳。
Code
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
template<class T>inline void Rd(T &x){
x=0;char c;
while((c=getchar())<48);
do x=(x<<1)+(x<<3)+(c^48);
while((c=getchar())>47);
}
const int S=18,N=100005;
int n,m;
vector<int>E[N];
int Rt[N],sgID[N],tim;
int fa[N][S];
int sum[N<<2];
bool mark[N];
int ans;
#define lson L,mid,p<<1
#define rson mid+1,R,p<<1|1
void update(int L,int R,int p,int&x){
if(!x or sum[p]==R-L+1) return;
if(L==R){
sum[p]=1;
mark[L]=1;
if(!--x) ans=L;
return;
}
int mid=(L+R)>>1;
update(lson,x),update(rson,x);
sum[p]=sum[p<<1]+sum[p<<1|1];
}
void del(int L,int R,int p,int x){
--sum[p];
if(L==R) return;
int mid=(L+R)>>1;
if(x<=mid)del(lson,x);
else del(rson,x);
}
void dfs(int x,int f){
fa[x][0]=f;
SREP(i,0,E[x].size()) if(E[x][i]!=f) dfs(E[x][i],x);
sgID[Rt[x]=++tim]=x;
}
int main(){
// freopen("queue.in","r",stdin);
// freopen("queue.out","w",stdout);
Rd(n),Rd(m);
SREP(i,1,n){
int a,b;
Rd(a),Rd(b);
E[a].push_back(b);
E[b].push_back(a);
}
REP(i,1,n) sort(E[i].begin(),E[i].end());
dfs(1,0);
SREP(j,1,S) REP(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
while(m--){
int op,x;
Rd(op),Rd(x);
if(op==1){
update(1,n,1,x);
printf("%d\n",sgID[ans]);
}
else{
ans=0;
DREP(k,S-1,0) if(mark[Rt[fa[x][k]]]) x=fa[x][k],ans|=1<<k;
mark[Rt[x]]=0;
del(1,n,1,Rt[x]);
printf("%d\n",ans);
}
}
return 0;
}