http://acm.hdu.edu.cn/showproblem.php?pid=5029
2 4 1 2 1 1 1 1 2 2 2 2 2 2 2 1 5 3 1 2 3 1 3 4 5 3 2 3 3 1 5 2 3 3 3 0 0
1 2 2 3 3 0 2
题意:给出一棵树,然后有一系列的操作,每次操作有a,b,c代表在a到b的路径上的每个点都给一个c类型的数字;最后问所有操作完成后树上的每个节点拥有的数量最多的类型是多少,如果存在多个输出较小类型的数字;
分析:首先这种在树上的操作,很容易想到是树链剖分的题目,但是具体该怎么写呢:先不考虑树的问题,可以把题意抽象出一个普通的连续区间【1,n】,然后在不同区间进行分配东西,最后问每个点的结果,对于区间【L,R】上分配一个数字Z,那么就在L位置上标记+Z,代表从此时以后的每个节点都拥有一个Z,然后在R+1的位置上标记一个-Z,代表从该节点开始没有分配Z这个数字;所以先把m个操作记录下来,对于每个标记的节点,可以用一个vector容器记录所标记的东西,接下来我们可以建立一个权值线段树区间【1,100000】;每个点分别代表数字Z的类型,px数组代表每个类型数字当前出现的次数;tree[i].maxi代表i区间的最大值,从左向右遍历区间【1,n】,对于每个节点更新px的值,如果标记是+Z,则px[z]++;否则px[-z]--;然后query即可;
树链剖分也是同样的道理,轻重链的划分不详说了,对于一个树上的两点<u,v>,把它转化为多条连续的子链利用线段树操作即可;具体程序如下:
#pragma comment(linker, "/STACK:1024000000,1024000000") #include"stdio.h" #include"string.h" #include"iostream" #include"map" #include"string" #include"queue" #include"stdlib.h" #include"math.h" #define M 110009 #define eps 1e-10 #define inf 1000000000 #define mod 1000000000 #define INF 1000000000 using namespace std; struct node { int u,v,next; }edge[M*2]; struct Lnode { int v; Lnode(int vv) { v=vv; } }; vector<Lnode>e[M]; int t,pos,head[M],son[M],fa[M],num[M],top[M],ID,deep[M],p[M],fp[M],px[M]; void init() { t=pos=0; memset(head,-1,sizeof(head)); memset(son,-1,sizeof(son)); memset(fa,-1,sizeof(fa)); } void add(int u,int v) { edge[t].u=u; edge[t].v=v; edge[t].next=head[u]; head[u]=t++; } void dfs(int u,int f,int d) { deep[u]=d; fa[u]=f; num[u]=1; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(v==f)continue; dfs(v,u,d+1); num[u]+=num[v]; if(son[u]==-1||num[son[u]]<num[v]) son[u]=v; } } void getpos(int u,int sp) { top[u]=sp; p[u]=pos++; fp[p[u]]=u; if(son[u]==-1)return; getpos(son[u],sp); for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(v==fa[u]||v==son[u])continue; getpos(v,v); } } /**********以上是划分轻重链**************/ struct Tree { int l,r,maxi; }tree[M*4]; void make(int l,int r,int i)//线段树建树操作 { tree[i].l=l; tree[i].r=r; if(l==r) { tree[i].maxi=0; return ; } int mid=(l+r)/2; make(l,mid,i*2); make(mid+1,r,i*2+1); tree[i].maxi=max(tree[i*2].maxi,tree[i*2+1].maxi); } void updata(int p,int q,int i)//单点更新操作 { if(tree[i].l==p&&tree[i].r==p) { tree[i].maxi=q; return; } int mid=(tree[i].l+tree[i].r)/2; if(p<=mid)updata(p,q,i*2); else updata(p,q,i*2+1); tree[i].maxi=max(tree[i*2].maxi,tree[i*2+1].maxi); } void query(int l,int r,int i)//区间查找最大值对应的标,若有多个查找编号最小的记录到ID中 { if(tree[i].maxi<tree[1].maxi) return; if(tree[i].l==tree[i].r) { if(tree[i].l<ID) ID=tree[i].l; return; } int mid=(tree[i].l+tree[i].r)/2; if(r<=mid) query(l,r,i*2); else if(l>mid) query(l,r,i*2+1); else { query(l,mid,i*2); query(mid+1,r,i*2+1); } } void Insert(int u,int v,int w)//树与线段树的转化操作 { int f1=top[u]; int f2=top[v]; while(f1!=f2) { if(deep[f1]<deep[f2]) { swap(f1,f2); swap(u,v); } e[p[f1]].push_back(Lnode(w)); e[p[u]+1].push_back(Lnode(-w)); u=fa[f1]; f1=top[u]; } if(u==v) { e[p[u]].push_back(Lnode(w)); e[p[u]+1].push_back(Lnode(-w)); return; } if(deep[u]>deep[v]) swap(u,v); e[p[u]].push_back(Lnode(w)); e[p[v]+1].push_back(Lnode(-w)); return; } int s[M]; int main() { int n,m,i,j,a,b,c,u,v; while(scanf("%d%d",&n,&m),m||n) { init(); for(i=0;i<=n;i++) e[i].clear(); for(i=1;i<n;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs(1,1,1); getpos(1,1); for(i=1;i<=m;i++) { scanf("%d%d%d",&a,&b,&c); Insert(a,b,c); } make(1,100000,1); memset(px,0,sizeof(px)); for(i=0;i<pos;i++) { for(j=0;j<(int)e[i].size();j++) { int v=e[i][j].v; if(v>0) { px[v]++; updata(v,px[v],1); } else { px[-v]--; updata(-v,px[-v],1); } } if(tree[1].maxi==0) { s[fp[i]]=0; continue; } ID=100000; query(1,100000,1); s[fp[i]]=ID; //printf("%d\n",ID); } for(i=1;i<=n;i++) printf("%d\n",s[i]); } return 0; }