Bzoj 3307 雨天的尾巴(线段树合并+树上差分)
C. 雨天的尾巴
题目描述
输入格式
输出格式
样例
样例输入
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
20 50 8 6 10 6 18 6 20 10 7 20 2 18 19 8 1 6 14 20 16 10 13 19 3 14 17 18 11 19 4 11 15 14 5 18 9 10 12 15 11 14 87 12 1 87 14 3 84 17 2 36 6 5 93 17 6 87 10 14 93 5 16 78 6 15 93 15 5 16 11 8 50 17 19 50 5 4 87 15 20 78 1 17 50 20 13 87 7 15 22 16 11 94 19 8 87 18 3 93 13 13 87 2 1 87 2 6 22 5 20 84 10 12 93 18 12 87 16 10 93 8 17 93 14 7 36 7 4 22 5 9 87 13 10 16 20 11 50 9 16 84 10 17 16 19 6 87 12 2 36 20 9 94 9 2 84 14 1 94 5 5 94 8 17 16 12 8 36 20 17 78 12 18 50 16 8 94 2 19 36 10 18 36 14 19 50 4 12 50
样例输出
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
87 36 84 22 87 87 22 50 84 87 50 36 87 93 36 94 16 87 50 50
数据范围与提示
暴力能得50分呢……
树上操作首先会想到树剖和树上差分吧,这里只说差分;
离线处理,权值线段树维护每一个点的状态(每种物品出现次数及其最大值),对于每次操作,将x+1,y+1,LCA(x,y)-1,fa[LCA]-1最后dfs合并线段树统计答案即可。
注意合并(修改)叶子节点时最大值是加而不是取max。
这道题比较恶心的是卡内存,卡了我四节课…
如果线段树合并操作是建新节点的话会MLE,代码如下:
int merge(int x,int y) { if(!x||!y)return x+y; int now=++cnt; sum(now)=sum(x)+sum(y); l(now)=merge(l(x),l(y)); r(now)=merge(r(x),r(y)); if(!l(x) && !r(x))maxn(now)=maxn(x)+maxn(y); else maxn(now)=max( maxn(l(now)) , maxn(r(now)) ); return now; }
但是显然不这样的话数据会出错(将y的子树同时变为x的子树,之后在合并x时会修改数据),但是其实并不需要让线段树最后是正确的,只需要在y数据发生错误之前记录答案即可。
标程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<map> #include<time.h> #include<cstdlib> #include<algorithm> using namespace std; struct edge { int u,v,next; #define u(x) ed[x].u #define v(x) ed[x].v #define n(x) ed[x].next }ed[200010]; int first[100010],num_e; #define f(x) first[x] int n,m,Q,fa[100010][21],bin[21],dep[100010]; int x[100010],y[100010],z[100010],z2[100010]; map<int,int> mp; int mmp[100010]; int ans[100010]; struct tree { int l,r,sum,maxn; #define l(x) tr[x].l #define r(x) tr[x].r #define sum(x) tr[x].sum #define maxn(x) tr[x].maxn }tr[20000000]; int cnt,rt[100010]; int ask(int l,int r,int a) { if(sum(a)==0)return 0; if(l==r)return l; int mid=(l+r)>>1; if(maxn(l(a))>=maxn(r(a)))return ask(l,mid,l(a)); return ask(mid+1,r,r(a)); } void add(int &mark,int l,int r,int loc,int val) { if(!mark)mark=++cnt; if(l==r){sum(mark)+=val;maxn(mark)+=val;return;} int mid=(l+r)>>1; if(loc<=mid)add(l(mark),l,mid,loc,val); else add(r(mark),mid+1,r,loc,val); sum(mark)=sum(l(mark))+sum(r(mark)); maxn(mark)=max( maxn(l(mark)) , maxn(r(mark))); } int merge(int x,int y) { if(!x||!y)return x+y; l(x)=merge(l(x),l(y)); r(x)=merge(r(x),r(y)); sum(x)=sum(x)+sum(y); if(!l(x) && !r(x))maxn(x)=maxn(x)+maxn(y); else maxn(x)=max( maxn(l(x)) , maxn(r(x)) ); return x; } void dfs2(int x,int ffa); inline int read(); int LCA(int x,int y); void dfs(int x,int ffa); inline void add_e(int u,int v); signed main() { // freopen("4.in","r",stdin); // freopen("out.txt","w",stdout); bin[0]=1; for(int i=1;i<=20;i++)bin[i]=bin[i-1]*2; n=read(),Q=read(); int ta,tb; for(int i=1;i<n;i++) { ta=read(),tb=read(); add_e(ta,tb); add_e(tb,ta); } for(int j=1;j<=Q;j++) x[j]=read(),y[j]=read(),z[j]=read(),z2[j]=z[j]; sort(z2+1,z2+Q+1); m=unique(z2+1,z2+Q+1)-z2-1; for(int i=1;i<=Q;i++) { int loc=lower_bound(z2+1,z2+m+1,z[i])-z2; mp[z[i]]=loc; mmp[loc]=z[i]; } dfs(1,0); for(int j=1;j<20;j++) for(int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; mmp[0]=0; for(int i=1;i<=Q;i++) { int loc=mp[z[i]], lca=LCA(x[i],y[i]), ffa=fa[lca][0]; add(rt[x[i]],1,m,loc,1); add(rt[y[i]],1,m,loc,1); add(rt[lca], 1,m,loc,-1); if(ffa) add(rt[ffa] ,1,m,loc,-1); } dfs2(1,0); ans[1]=ask(1,m,rt[1]); for(int i=1;i<=n;i++) printf("%d\n",mmp[ans[i]]); } void dfs2(int x,int ffa) { for(int i=f(x);i;i=n(i)) if(v(i)!=ffa) { dfs2(v(i),x); ans[v(i)]=ask(1,m,rt[v(i)]); rt[x]=merge(rt[x],rt[v(i)]); } } inline int read() { int s=0;char a=getchar(); while(a<'0'||a>'9')a=getchar(); while(a>='0'&&a<='9'){s=s*10+a-'0';a=getchar();} return s; } inline void add_e(int u,int v) { ++num_e; u(num_e)=u; v(num_e)=v; n(num_e)=f(u); f(u)=num_e; } void dfs(int x,int ffa) { fa[x][0]=ffa; dep[x]=dep[ffa]+1; for(int i=f(x);i;i=n(i)) if(v(i)!=ffa) dfs(v(i),x); } int LCA(int x,int y) { if(dep[x]>dep[y])swap(x,y); while(dep[x]!=dep[y]) for(int i=0;;i++) if(dep[fa[y][i]]<dep[x]) { y=fa[y][i-1]; break; } if(x==y)return x; while(fa[x][0]!=fa[y][0]) for(int i=0;;i++) if(fa[x][i]==fa[y][i]) {x=fa[x][i-1],y=fa[y][i-1];break;} return fa[x][0]; }
波澜前,面不惊。