Description
带修树链第K大。\((n,q\leq 80000)\)
Solution
据说有很多种方法?
写了两种。
- 树剖套线段树套平衡树,外层还需要个二分,复杂度\(O(n\log^4n)\)
- \(dfs\)序上维护树状数组套主席树,复杂度\(O(n\log^2n)\)
第一种就是比较裸的树套树吧,注意二分的\(check(x)\)定义是当答案为\(x\)时这条链上是否有至少\(k\)个大于\(x\)的点。
然后平衡树上查就行了。
试了几个\(srand\)发现我的生日加上xxx的生日跑的最快...
第二种就可以说一说了
先考虑无修改树链第\(k\)大怎么求,显然每个点的主席树存下了这个点到根的路径上所有点的权值。然后就是\(sum[x]+sum[y]-sum[lca(x,y)]-sum[fa[lca(x,y)]]\)。
如果有修改,那么改一个点时会影响到子树内的\(O(n)\)棵主席树。
但是观察到一个点的子树的\(dfs\)序是一段区间,可以区间加这个玩意儿。
然而区间主席树加某个值并不好加,我们可以在差分序列上变成单点加。然后求和的时候求一个前缀和就行了。具体地,我们可以把求这四个点(\(x,y,lca(x,y),fa[lca(x,y)]\))的前缀和所需要用到的点都存进一个数组里面,然后主席树求\(k\)大的时候就不是四个点同时往下走而是这个数组里面所有的点同时往下走了。
然后要记得输出原本的值不要输出离散化之后的值。。。
别的就没啥了,发现自己已经zz到看一个单点加都要思考半天为什么要用树状数组了。。。。
Code
\(O(n\log^4n)\)代码这奇怪的变量名奇怪的函数名和奇怪的缩进是我想写namespace无果造成的,我的码风还是正常的你们要相信我啊喂
#include<cctype>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
const int N=80005;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define mp(A,B) std::make_pair(A,B)
int root[N<<2];
int head[N],dfn[N],val[N];
int n,m,cnt,fa[N],d[N],tot;
int top[N],sze[N],son[N],fs[N];
struct Edge{
int to,nxt;
}edge[N<<1];
void add(int x,int y){
edge[++cnt].to=y;
edge[cnt].nxt=head[x];
head[x]=cnt;
}
int ttot;
int delpool[N*20],delcur;
int tsze[N*20],tprio[N*20];
int tch[N*20][2],tval[N*20];
void tpushup(int cur){
tsze[cur]=tsze[tch[cur][0]]+tsze[tch[cur][1]]+1;
}
void trotate(int &x,int d){
int y=tch[x][d],z=tch[y][d^1];
tch[y][d^1]=x;tch[x][d]=z;
tpushup(x);tpushup(y);x=y;
}
int newnode(){
int t=delcur?delpool[delcur--]:++ttot;
tch[t][0]=tch[t][1]=tval[t]=tprio[t]=tsze[t]=0;return t;
}
void tinsert(int &cur,int x){
if(!cur){
cur=newnode();
tsze[cur]=1;tval[cur]=x;tprio[cur]=rand();
return;
}
int d=x>tval[cur];
tsze[cur]++;
tinsert(tch[cur][d],x);
if(tprio[tch[cur][d]]<tprio[cur])
trotate(cur,d);
}
void tremove(int &cur,int x){
if(tval[cur]==x){
if(!tch[cur][0] or !tch[cur][1]){
delpool[++delcur]=cur;
cur=tch[cur][0]+tch[cur][1];
return;
}
if(tprio[tch[cur][0]]<tprio[tch[cur][1]])
trotate(cur,0),tremove(tch[cur][1],x);
else trotate(cur,1),tremove(tch[cur][0],x);
tpushup(cur);
} else{
int d=x>tval[cur];
tremove(tch[cur][d],x);
tpushup(cur);
}
}
int trank(int cur,int x){
if(!cur) return 0;
if(tval[cur]>=x) return tsze[tch[cur][1]]+1+trank(tch[cur][0],x);
else return trank(tch[cur][1],x);
}
void dfs(int x,int y){
if(!x) return;
tinsert(root[y],tval[x]);
dfs(tch[x][0],y);
dfs(tch[x][1],y);
}
void build(int cur,int l,int r){
if(l==r){
tinsert(root[cur],val[fs[l]]);
return;
}
int mid=l+r>>1;
build(cur<<1,l,mid);build(cur<<1|1,mid+1,r);
dfs(root[cur<<1],cur);dfs(root[cur<<1|1],cur);
}
void modify(int cur,int l,int r,int ql,int qr,int minus,int add){
tremove(root[cur],minus);tinsert(root[cur],add);
if(l==r) return;
int mid=l+r>>1;
if(ql<=mid) modify(cur<<1,l,mid,ql,qr,minus,add);
else modify(cur<<1|1,mid+1,r,ql,qr,minus,add);
}
int query(int cur,int l,int r,int ql,int qr,int c){
if(ql<=l and r<=qr)
return trank(root[cur],c);
int mid=l+r>>1,ans=0;
if(ql<=mid) ans+=query(cur<<1,l,mid,ql,qr,c);
if(mid<qr) ans+=query(cur<<1|1,mid+1,r,ql,qr,c);
return ans;
}
int getint(){
int X=0,w=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
return w?-X:X;
}
void dfs1(int now){
sze[now]=1;
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(sze[to]) continue;
fa[to]=now;d[to]=d[now]+1;
dfs1(to);sze[now]+=sze[to];
son[now]=sze[to]>sze[son[now]]?to:son[now];
}
}
void dfs2(int now,int low){
dfn[now]=++tot;top[now]=low;fs[tot]=now;
if(son[now]) dfs2(son[now],low);
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(dfn[to]) continue;
dfs2(to,to);
}
}
bool check(int mid,int k,int x,int y){
int ans=0,fir=d[x]+d[y];
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]])
swap(x,y);
ans+=query(1,1,n,dfn[top[x]],dfn[x],mid);
x=fa[top[x]];
}
if(d[x]<d[y]) swap(x,y);
ans+=query(1,1,n,dfn[y],dfn[x],mid);
fir-=d[y]*2;fir++;
if(fir<k) return 0;
if(ans>=k) return 1;
return 0;
}
signed main(){
srand(20020122);
n=getint(),m=getint();
for(int i=1;i<=n;i++)
val[i]=getint();
for(int i=1;i<n;i++){
int x=getint(),y=getint();
add(x,y);add(y,x);
}
d[1]=1;dfs1(1);dfs2(1,1);build(1,1,n);
while(m--){
int k=getint(),x=getint(),y=getint();
if(!k){
modify(1,1,n,dfn[x],dfn[x],val[x],y);
val[x]=y;
} else{
int l=0,r=1e8,ans=1e8;
while(l<=r){
int mid=l+r>>1;
if(check(mid,k,x,y)) ans=mid,l=mid+1;
else r=mid-1;
} printf(ans==1e8?"invalid request!\n":"%d\n",ans);
}
} return 0;
}
然后是\(O(n\log^2n)\)的
#include<cctype>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
const int N=80005;
typedef double db;
typedef long long ll;
const int M=10000010;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define mp(A,B) std::make_pair(A,B)
int s1[N<<1],s2[N<<1];
int n,m,cnt,tot,val[N],sze[N];
int k[N],x[N],y[N],g[N<<1],len;
int ch[M][2],root[N],f[N][19];
int head[N],sum[M],dfn[N],d[N];
struct Edge{
int to,nxt;
}edge[N<<1];
void add(int x,int y){
edge[++cnt].to=y;
edge[cnt].nxt=head[x];
head[x]=cnt;
}
int getint(){
int X=0,w=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
return w?-X:X;
}
void dfs(int now){
sze[now]=1;dfn[now]=++tot;
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(sze[to]) continue;
f[to][0]=now;d[to]=d[now]+1;
for(int j=1;j<=17;j++) f[to][j]=f[f[to][j-1]][j-1];
dfs(to);sze[now]+=sze[to];
}
}
void pushup(int cur){
sum[cur]=sum[ch[cur][0]]+sum[ch[cur][1]];
}
void modify(int &cur,int ql,int c,int l=1,int r=len){
if(!cur) cur=++tot;
if(l==r){sum[cur]+=c;return;}
int mid=l+r>>1;
if(ql<=mid) modify(ch[cur][0],ql,c,l,mid);
else modify(ch[cur][1],ql,c,mid+1,r);
pushup(cur);
}
void add(int x,int y,int z){
while(x<=n)
modify(root[x],y,z),x+=x&-x;
}
int lca(int x,int y){
if(d[x]<d[y]) swap(x,y);
for(int i=17;~i;i--)
if(d[f[x][i]]>=d[y]) x=f[x][i];
if(x==y) return x;
for(int i=17;~i;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int query(int k,int l=1,int r=len){
if(l==r) return l;
int now=0,mid=l+r>>1;
for(int i=1;i<=s1[0];i++) now+=sum[ch[s1[i]][1]];
for(int i=1;i<=s2[0];i++) now-=sum[ch[s2[i]][1]];
if(now>=k){
for(int i=1;i<=s1[0];i++) s1[i]=ch[s1[i]][1];
for(int i=1;i<=s2[0];i++) s2[i]=ch[s2[i]][1];
return query(k,mid+1,r);
} else{
for(int i=1;i<=s1[0];i++) s1[i]=ch[s1[i]][0];
for(int i=1;i<=s2[0];i++) s2[i]=ch[s2[i]][0];
return query(k-now,l,mid);
}
}
void ask(int k,int a,int b){
int c=lca(a,b),dd=f[c][0];s1[0]=s2[0]=0;
if(d[a]+d[b]-d[c]-d[dd]<k) {printf("invalid request!\n");return;}
for(int i=dfn[a];i;i-=i&-i) s1[++s1[0]]=root[i];
for(int i=dfn[b];i;i-=i&-i) s1[++s1[0]]=root[i];
for(int i=dfn[c];i;i-=i&-i) s2[++s2[0]]=root[i];
for(int i=dfn[dd];i;i-=i&-i) s2[++s2[0]]=root[i];
printf("%d\n",g[query(k)]);
}
signed main(){
n=getint(),m=getint();
for(int i=1;i<=n;i++) val[i]=getint(),g[++len]=val[i];
for(int i=1;i<n;i++){
int x=getint(),y=getint();
add(x,y);add(y,x);
}
for(int i=1;i<=m;i++){
k[i]=getint(),x[i]=getint(),y[i]=getint();
if(!k[i]) g[++len]=y[i];
}
std::sort(g+1,g+1+len);len=std::unique(g+1,g+1+len)-g-1;
for(int i=1;i<=n;i++) val[i]=std::lower_bound(g+1,g+1+len,val[i])-g;
for(int i=1;i<=m;i++) if(!k[i]) y[i]=std::lower_bound(g+1,g+1+len,y[i])-g;
d[1]=1;dfs(1);tot=0;
for(int i=1;i<=n;i++) add(dfn[i],val[i],1),add(dfn[i]+sze[i],val[i],-1);
for(int i=1;i<=m;i++){
if(!k[i]) add(dfn[x[i]],val[x[i]],-1),add(dfn[x[i]]+sze[x[i]],val[x[i]],1),val[x[i]]=y[i],add(dfn[x[i]],val[x[i]],1),add(dfn[x[i]]+sze[x[i]],val[x[i]],-1);
else ask(k[i],x[i],y[i]);
} return 0;
}