题目意思非常清楚,就是要求树上带修改的路径k大值
如果不带修改的话,我会用树上主席树去搞,以父子关系建树,可以参见 [BZOJ 3123]森林
但是带修改就不会打了QAQ,于是去学了另一种在dfs序上搞的方法(同时感谢呵呵酵母菌的帮助)
其实思想是一样的,就是搞出来节点到根路径的线段树,然后用x+y-lca-fa(lca)去差分出来树上路径的线段树,再去里面查询k值
那么我们怎么得到点到根路径的线段树呢?可以在dfs序上差分啊!
dfs序列上的dfnl[x]~dfnr[x]包含的是以x为根节点的子树的dfs序(这个学过dfs序的应该都知道)
对于一个节点x,它的值只会对于它的子树内的点造成贡献,于是我们在dfs序上dfnl[x]的位置+1,dfnr[x]+1的位置-1,这样我们求前缀和的时候就会把这个子树内的值给差分掉
意思就是说,求x到root的路径线段树,我们求dfnl[x]的前缀和线段树就好了,因为这个路径之外的子树或节点,要么在dfnl[x]的后边,要么就被前缀和差分掉了
因为带修改,所以用树状数组维护。然后我就没有打主席树,而是打树状数组套动态开点线段树(相当于直接把线段树类比数组来用了)
代码:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cstdlib>#define pos(i,a,b) for(int i=(a);i<=(b);i++)#define N 101000#define lc(x) (tree[x].lc)#define rc(x) (tree[x].rc)#include<vector>using namespace std;vector<int> b;int lowbit(int x){return x&(-x);}int findx(int x){return lower_bound(b.begin(),b.end(),x)-b.begin()+1;}int n,q,a[N],root[N],size,len;int p[N][20],dep[N],fa[N];struct haha{int lc,rc,sum;}tree[N*100];struct xixi{int next,to;}edge[N*2];int head[N],cnt=1;void add2(int u,int v){edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}int dfnl[N],dfnr[N],ji;void dfs(int x){dfnl[x]=++ji;for(int i=head[x];i;i=edge[i].next){int to=edge[i].to;if(to!=fa[x]){fa[to]=x;dep[to]=dep[x]+1;dfs(to);}}dfnr[x]=ji;}void init(){for(int j=0;(1<<j)<=n;j++) pos(i,1,n) p[i][j]=-1;pos(i,1,n) p[i][0]=fa[i];for(int j=1;(1<<j)<=n;j++){pos(i,1,n){if(p[i][j-1]!=-1){p[i][j]=p[p[i][j-1]][j-1];}}}}int lca(int a,int b){if(dep[a]<dep[b]) swap(a,b);int i;for(i=0;(1<<i)<=dep[a];i++);i--;for(int j=i;j>=0;j--){if(dep[a]-(1<<j)>=dep[b]) a=p[a][j];}if(a==b) return a;for(int j=i;j>=0;j--){if(p[a][j]!=p[b][j]&&p[a][j]!=-1){a=p[a][j];b=p[b][j];}}return fa[a];}struct nono{int k,a,b;}ques[N];void update(int &rt,int l,int r,int pos,int num){if(!rt) rt=++size;tree[rt].sum+=num;if(l==r) return;int mid=(l+r)>>1;if(pos<=mid) update(lc(rt),l,mid,pos,num);else update(rc(rt),mid+1,r,pos,num);tree[rt].sum=tree[lc(rt)].sum+tree[rc(rt)].sum;}void add(int x,int pos,int num){while(x<=n){update(root[x],1,len,pos,num);x+=lowbit(x);}}int cunx[200],cuny[200],cunlca[200],cunfalca[200];int query(int l,int r,int k){if(l==r) return l;int sumx(0),sumy(0),sumlca(0),sumfalca(0);pos(i,1,cunx[0]){sumx+=tree[lc(cunx[i])].sum;}pos(i,1,cuny[0]){sumy+=tree[lc(cuny[i])].sum;}pos(i,1,cunlca[0]){sumlca+=tree[lc(cunlca[i])].sum;}pos(i,1,cunfalca[0]){sumfalca+=tree[lc(cunfalca[i])].sum;}int mid=(l+r)>>1;int temp=sumx+sumy-sumlca-sumfalca;if(temp>=k){pos(i,1,cunx[0]){cunx[i]=lc(cunx[i]);}pos(i,1,cuny[0]){cuny[i]=lc(cuny[i]);}pos(i,1,cunlca[0]){cunlca[i]=lc(cunlca[i]);}pos(i,1,cunfalca[0]){cunfalca[i]=lc(cunfalca[i]);}return query(l,mid,k);} else{pos(i,1,cunx[0]){cunx[i]=rc(cunx[i]);}pos(i,1,cuny[0]){cuny[i]=rc(cuny[i]);}pos(i,1,cunlca[0]){cunlca[i]=rc(cunlca[i]);}pos(i,1,cunfalca[0]){cunfalca[i]=rc(cunfalca[i]);}return query(mid+1,r,k-temp);} }int la;int Query(int x,int y,int k){int falc=fa[la];cunx[0]=cuny[0]=cunlca[0]=cunfalca[0]=0;int tx=dfnl[x],ty=dfnl[y],tlca=dfnl[la],tfalc=dfnl[falc];while(tx>0){cunx[++cunx[0]]=root[tx];tx-=lowbit(tx);}while(ty>0){cuny[++cuny[0]]=root[ty];ty-=lowbit(ty);}while(tlca>0){cunlca[++cunlca[0]]=root[tlca];tlca-=lowbit(tlca);}while(tfalc>0){cunfalca[++cunfalca[0]]=root[tfalc];tfalc-=lowbit(tfalc);}return query(1,len,k);}int main(){scanf("%d%d",&n,&q);len=n+q;pos(i,1,n) scanf("%d",&a[i]),b.push_back(a[i]);pos(i,1,n-1){int x,y;scanf("%d%d",&x,&y);add2(x,y);add2(y,x);}pos(i,1,q){scanf("%d%d%d",&ques[i].k,&ques[i].a,&ques[i].b);if(ques[i].k==0){b.push_back(ques[i].b);}}sort(b.begin(),b.end());b.erase(unique(b.begin(),b.end()),b.end());dfs(1);init();pos(i,1,n){add(dfnl[i],findx(a[i]),1);add(dfnr[i]+1,findx(a[i]),-1);}pos(i,1,q){int k=ques[i].k,x=ques[i].a,y=ques[i].b;if(!k){add(dfnl[x],findx(a[x]),-1);add(dfnr[x]+1,findx(a[x]),1);add(dfnl[x],findx(y),1);add(dfnr[x]+1,findx(y),-1);a[x]=y;}else{la=lca(x,y);int jishu=dep[x]+dep[y]-2*dep[la]+1;if(jishu<k) printf("invalid request!\n");else printf("%d\n",b[Query(x,y,jishu-k+1)-1]);}}return 0;}
感悟:
这道题为我提供了一个新的思路,就是可以把树上的东西搞到dfs序上(原来也一直知道但是从来没打过T-T),因为在序列上可以用高级数据结构搞事
在dfs序上差分,求出节点到root想要的答案,然后再在树上差分。妙啊!
比如说最简单的,求带修改树上路径长度,我们可以用这种方法代替树链剖分(时间复杂度理论分析O(nlogn),似乎要快啊OvO)
嗯。学习到了。
[BZOJ 1146]网络管理Network 树上带修改路径k值
原文地址:http://www.cnblogs.com/Hallmeow/p/8000316.html