雨天的尾巴
考试的时候直接扎第一题上了这到题连暴力都没打出来T_T;
心路历程:
其实考试时候还是有可能做出来的,当然关键在能否想到线段树合并。
当时想到了离散化(很慌没打出来。。。),树上差分,lca倍增,当时觉滴倍增很难打,一看n<100000,于是选择
用向上标记法,然而少了一行代码,,,,爆零两行泪。。。
现在看来倍增真是一点不难啊好打有好用,所以不要有为难情绪,刚就完了。(平日板子要多打记熟啊)
之所以没想到线段树合并是因为当时真的没有透彻理解,
所以新知识点还是要知道它能干什么,知道它的用处。
离散化用来干掉1->10^9,考试的时候真的傻认为要是有10^9种不就完了吗,,,然后发现忘了还有m次操作这东东,所以z离散化后len最大也就m个卡掉了很多
以前一个物品的题直接dfs,lca树上差分就行,现在有10^9个(离散后10^5个),空间时间不可能一个一个去维护所以 得用线段树合并。
所以得离线来做(我承认现在才知道离线在线是嘛玩意。。丢人啊。。。)
每个点有很多信息所以权值线段树用来优化空间(当然也能优化时间),对每个点动态开树插入和删除(lca,f[lca]),最后dfs,父亲合并儿子。
所以需维护区间的maxcnt以及其id,在merge时就需要多传一下l,r(1,len),如果l==r更新maxx值和id,需要注意的是如果maxx<=0,id干成0(题目要求)。
sd错误:merge时忘了加上root[x]=merge,如果root[x]=0,就会...炸。还有空间没开够得开到maxn*50。
优化:插入时如果lca==x||lca==y,那就插入了一次删了一次所以干脆不插入。。。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn=100050; bool mark[maxn]; int n,m,t,ans[maxn],len; int kpx[maxn],kpy[maxn],kpz[maxn],turn[maxn];//离线离散化 int deep[maxn],f[maxn][20];//倍增lca int sz,root[maxn],cnt[maxn*50],lc[maxn*50],rc[maxn*50],maxx[maxn*50],num[maxn*50];//线段树 int head[maxn],cntn=1; struct node{ int to,next; }line[maxn*2]; void add(int x,int y) { line[cntn].to=y; line[cntn].next=head[x]; head[x]=cntn++; } void dfs(int x) { for(int i=head[x];i;i=line[i].next) { int v=line[i].to; if(!mark[v]) { mark[v]=1; deep[v]=deep[x]+1; f[v][0]=x; for(int j=1;j<=t;j++) f[v][j]=f[f[v][j-1]][j-1]; dfs(v); } } } int ask(int x,int y) { if(deep[x]>deep[y]) swap(x,y); for(int j=t;j>=0;j--) if(deep[f[y][j]]>=deep[x]) y=f[y][j]; if(x==y) return x; for(int j=t;j>=0;j--) { if(f[x][j]!=f[y][j]) { x=f[x][j]; y=f[y][j]; } } return f[x][0]; } void insert(int &root,int l,int r,int x) { if(!root) root=++sz; cnt[root]++; if(l==r) { maxx[root]=cnt[root]; num[root]=maxx[root]<=0?0:l; return; } int mid=(l+r)/2; if(x<=mid) insert(lc[root],l,mid,x); else insert(rc[root],mid+1,r,x); if(maxx[lc[root]]>=maxx[rc[root]]) maxx[root]=maxx[lc[root]],num[root]=num[lc[root]]; else maxx[root]=maxx[rc[root]],num[root]=num[rc[root]]; } void decrease(int &root,int l,int r,int x) { if(!root) root=++sz; cnt[root]--; if(l==r) { maxx[root]=cnt[root]; num[root]=maxx[root]<=0?0:l; return; } int mid=(l+r)/2; if(x<=mid) decrease(lc[root],l,mid,x); else decrease(rc[root],mid+1,r,x); if(maxx[lc[root]]>=maxx[rc[root]]) maxx[root]=maxx[lc[root]],num[root]=num[lc[root]]; else maxx[root]=maxx[rc[root]],num[root]=num[rc[root]]; } int merge(int x,int y,int l,int r) { if(!x||!y) return x+y; cnt[x]+=cnt[y]; if(l==r) { maxx[x]=cnt[x]; num[x]=maxx[x]<=0?0:l; return x; } int mid=(l+r)/2; lc[x]=merge(lc[x],lc[y],l,mid); rc[x]=merge(rc[x],rc[y],mid+1,r); if(maxx[lc[x]]>=maxx[rc[x]]) maxx[x]=maxx[lc[x]],num[x]=num[lc[x]]; else maxx[x]=maxx[rc[x]],num[x]=num[rc[x]]; return x; } void dfst(int x) { for(int i=head[x];i;i=line[i].next) { int v=line[i].to; if(!mark[v]) { mark[v]=1; dfst(v); root[x]=merge(root[x],root[v],1,len); } } ans[x]=turn[num[root[x]]]; } int main(){ int x,y; scanf("%d%d",&n,&m); while( (1<<(t+1)) <=n) t++; for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } deep[1]=1; mark[1]=1; dfs(1); for(int i=1;i<=m;i++) { scanf("%d%d%d",&kpx[i],&kpy[i],&kpz[i]); turn[i]=kpz[i]; } sort(turn+1,turn+1+m); len=unique(turn+1,turn+1+m)-(turn+1); for(int i=1;i<=m;i++) { kpz[i]=lower_bound(turn+1,turn+1+len,kpz[i])-turn; } for(int i=1;i<=m;i++) { x=ask(kpx[i],kpy[i]); if(x==kpx[i]) { insert(root[ kpy[i] ],1,len,kpz[i]); decrease(root[ f[x][0] ],1,len,kpz[i]); continue; } if(x==kpy[i]) { insert(root[ kpx[i] ],1,len,kpz[i]); decrease(root[ f[x][0] ],1,len,kpz[i]); continue; } insert(root[ kpx[i] ],1,len,kpz[i]); insert(root[ kpy[i] ],1,len,kpz[i]); decrease(root[x],1,len,kpz[i]); decrease(root[ f[x][0] ],1,len,kpz[i]); } memset(mark,0,sizeof(mark)); mark[1]=1; dfst(1); for(int i=1;i<=n;i++) { printf("%d\n",ans[i]); } }