您的位置:首页 > 理论基础 > 数据结构算法

【LA7402】colorful tree 数据结构

2016-02-12 14:32 489 查看
As we all know, frogs live on trees and have different colors.

N frogs are living on a tree. The tree consists of N nodes with node 1 as the root, each frog occupies

a node.

Frogs have different colors, and can change colors as they like. On each day, all the frogs living on

a certain sub-tree will change its color. The root of the sub-tree, and the color they change to, is given

to the frog king.

As the frog king, sometimes he may wonder, how many different colors of frog are there in a certain

sub-tree? It turns to you to solve the problem for the king.

Input

First line contains an integer T, which indicates the number of test cases.

Every test case begins with an integers N, which is the numbers of nodes in the tree.

The following N − 1 lines describe the edges of the tree, and every line is formatted as ‘u v’, which

indicates there is a edge between node u and node v.

The next line contains N intergers, c1, c2, · · ·, cN , and ci

is the initial color of the frog living at

node i.

Then a number M follows, which indicates the number of queries, and following M lines describe

the quries as format bellow.

operation format description

modify color 0 u c change the color of all frogs in the sub-tree rooted at

node u to c

query 1 u query how many different colors of frog are there in

the sub-tree rooted at node u

Restrictions:

• 1 ≤ T ≤ 100.

• For 85% data, 1 ≤ N, M ≤ 1000.

• for 100% data, 1 ≤ N, M ≤ 105

.

• for every node, 1 ≤ ci ≤ N.

• for every edge, 1 ≤ u, v ≤ N.

• for every query, 1 ≤ u, c ≤ N.

Output

For every test case, you should output ‘Case #x:’ first, where x indicates the case number and counts

from 1.

Then for each query operation, output the number of different colors.

Sample Input

1

5

1 2

1 3

2 5

2 4

1 2 3 4 5

6

1 1

0 4 2

1 1

1 2

0 2 3

1 2

Sample Output

Case #1:

5

4

2

1

原题传送门

WC上刘汝佳先生推荐的题,当时和许多小伙伴想了许久,有人给出了一个好像还不错的做法(然而忘了……)后来给刘发了邮件询问,它给我发了一篇题解,看了之后总算大概明白该怎么做了。通过这个题真的是明白了 树上做的事情比起直接在序列上做,不见得更糟糕。 (想引用WC上营员交流时……好像是小火车说的话?“现在有些出题人,总喜欢把序列上的问题出到树上,让选手强行写个树链剖分……其实,出到仙人掌上也是可以的……希望大家不要拿这种东西出题……”),zgy神犇的一句话很好地解释了这个看起来不太合理的事实背后的原因:“子树只有n个(区间有O(n2)个)。”

让我们一步一步来分析。为了叙述方便,我们形式化定义一下修改:它是一个二元组(u,c),即把根节点为u的子树中所有结点的颜色全部修改为c。为了统一,不妨把所有结点的初始颜色也看作是一次次修改(注意初始化的时候需要从上至下)。在修改一棵子树的时候,我们需要暴力把之前存在于这棵子树内的修改依次找出来并暴力删除(显然总共只会删除O(n+m)次;同时为了做到这点,需要把所有修改按照u的深搜序排序)。显然,每个修改都会影响到u的所有的祖先结点,但是如何不重不漏的统计呢?其实,我们可以只需要考虑一棵子树中同中颜色的点中dfs序最大的点对它的影响就好了。为了做到这点,不妨采用下面的方法:把颜色相同的所有点按dfs序排好,设排列为a1,a2,a3,…..ak,那么,我们让a1影响 a1到路径b1上的点的答案,其中b1的父亲为lca(a1,a2),让a2影响 a2到b2路径上的点的答案,其中b2的父亲为lca(a2,a3)….以此类推,ak直接影响它到根的路径上的点就可以了(在我的代码中对这个的处理很丑……其实后来想了想其实是可以再在根上面加个超级根的……嗝,懒得改了……)。这样我们便保证了对于某种颜色我们只考虑了dfs序最大的点的影响。同中颜色的点集以及所有修改的集合都可以使用平衡树来维护(这里使用了set)。

询问某个点的时候,除了算上所有的修改对它的影响外,还要考虑:如果这个点不属于任何修改(即没有一个修改的u是它),我们需要知道它的真实颜色(这个可以使用线段树简单地维护),同时还要知道这种颜色是否已经出现在它子树的某个修改中(在那种颜色的平衡树上查找即可),来判断答案是否还要加一。

根据上面的叙述不难发现本题变成了树上的路径更新+单点查询。正常人看到这个都会使用树链剖分+树状数组(毕竟是单点,差分一下就好嘛,也不用啥高大上的区间修改……)(或线段树)或者LCT解决了吧(虽然这树剖的总复杂度是O(nlog2n)的,但实际上运行速度很快,难以达到这上界),但是后来我再次询问刘汝佳的时候他告诉我不需要用到树链剖分QAQ天辣树上路径更新本蒟蒻实在是想不到别的办法了……算惹以后再来填坑= =(这篇题解好长啊= =

#include<cstdio>
#include<iostream>
#include<cstring>
#include<set>
#define clr(a,b) memset(a,b,sizeof(a))
#define maxn 100000
#define mid (l+r>>1)
using namespace std;
struct EDGE{
int u,v,next;
}edge[2*maxn+10];
int head[maxn+10],pp;
void adde(int u,int v){
edge[++pp]=(EDGE){u,v,head[u]};
head[u]=pp;
}
int fa[maxn+10],de[maxn+10],top[maxn+10],id[maxn+10],sz[maxn+10],hs[maxn+10];
int son[maxn+10],bro[maxn+10];
int dfn[maxn+10],c[maxn+10],clo;
int n;
//线段树用来维护结点的实际颜色
struct segmentree{
int lc[2*maxn+10],rc[2*maxn+10],setv[2*maxn+10],cnt;
void init(){
clr(setv,-1);
int o;
cnt=0;
maketree(o,1,n);
}
void maketree(int &o,int l,int r){
o=++cnt;
if(l==r)return;
int m=mid;
maketree(lc[o],l,m);
maketree(rc[o],m+1,r);
}
void pushdown(int o){
if(setv[o]==-1)return;
setv[lc[o]]=setv[rc[o]]=setv[o];
setv[o]=-1;
}
void update(int o,int l,int r,int ql,int qr,int v){
if(ql<=l&&r<=qr)setv[o]=v;
else{
pushdown(o);
int m=mid;
if(ql<=m)update(lc[o],l,m,ql,qr,v);
if(qr>m)update(rc[o],m+1,r,ql,qr,v);
}
}
int query(int o,int l,int r,int p){
if(setv[o]!=-1)return setv[o];
int m=mid;
if(p<=m)return query(lc[o],l,m,p);
return query(rc[o],m+1,r,p);
}
}seg;
//小技巧get√不愿意写struct的懒鬼,虽然感觉这重载怪怪的……
struct cmp{bool operator()(const int&a,const int &b){return dfn[a]<dfn[b];}};
set<int,cmp> S[maxn+10],d;
struct fenwicktree{
int c[maxn+10];
void clear(){clr(c,0);}
void add(int x,int v){
while(x<=n){
c[x]+=v;
x+=x&-x;
}
}
int sum(int p){
int ret=0,cp=seg.query(1,1,n,dfn[p]);
set<int>::iterator it=S[cp].lower_bound(p);
//注意处理没有修改存在于这个点上的情况
if(d.find(p)==d.end()&&(it==S[cp].end()||dfn[*it]>=dfn[p]+sz[p]))ret=1;
int x=id[p];
while(x){
ret+=c[x];
x-=x&-x;
}
return ret;
}
}fen;
void update(int l,int r,int v){
fen.add(l,v);
fen.add(r+1,-v);
}
//这里真是异常的丑啊= =但是懒得改了
void lca(int u,int v,int val,int f){
for(;;){
if(top[u]==top[v]){
if(de[u]>=de[v])update(id[v]+(f==0),id[u],val);
break;
}else{
if(de[top[u]]>=de[top[v]]){
update(id[top[u]],id[u],val);
u=fa[top[u]];
}else v=fa[top[v]];
}
}
}
void del(int u){
set<int>::iterator it=S[c[u]].find(u);
int p=0,s;
if(it!=S[c[u]].begin()){
p=*(--it);
++it;
}
if(++it!=S[c[u]].end())s=*it;
else s=1;
if(p){
lca(p,u,-1,0);
lca(p,s,1,s==1);
}
lca(u,s,-1,s==1);
S[c[u]].erase(u);
}
void modify(int u,int nc){
seg.update(1,1,n,dfn[u],dfn[u]+sz[u]-1,nc);
set<int>::iterator it,it0;
//这里也丑,可是不这样的话会崩溃= =蜜汁迭代器 蜜汁删除
for(it=d.lower_bound(u);it!=d.end();){
int v=*it;
if(dfn[v]>=dfn[u]+sz[u])break;
del(v);
it0=it++;
d.erase(it0);
}
d.insert(u);
c[u]=nc;
S[nc].insert(u);
it=S[nc].find(u);
int p=0,s;
if(it!=S[nc].begin()){
p=*(--it);
++it;
}
//一定要小心处理没有后继的情况
if(++it!=S[nc].end())s=*it;
else s=1;
if(p){
lca(p,s,-1,s==1);
lca(p,u,1,0);
}
lca(u,s,1,s==1);
}
//两遍dfs完成树剖
void dfs1(int u){
dfn[u]=++clo;
sz[u]=1;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v;
if(v!=fa[u]){
bro[v]=son[u];
son[u]=v;
fa[v]=u;
de[v]=de[u]+1;
dfs1(v);
sz[u]+=sz[v];
if(sz[v]>sz[hs[u]])hs[u]=v;
}
}
}
void dfs2(int u,int t){
id[u]=++clo;
top[u]=t;
if(hs[u])dfs2(hs[u],t);
for(int v=son[u];v;v=bro[v])if(v!=hs[u]){
top[v]=v;
dfs2(v,v);
}
}
//颜色初始化
void dfs3(int u){
modify(u,c[u]);
for(int v=son[u];v;v=bro[v])dfs3(v);
}
int main(){
freopen("7402.in","r",stdin);
freopen("wode.out","w",stdout);
int T;
scanf("%d",&T);
for(int t=1;t<=T;t++){
printf("Case #%d:\n",t);
fen.clear();
d.clear();
pp=clo=0;
clr(head,0);
clr(hs,0);
clr(son,0);
scanf("%d",&n);
seg.init();
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
adde(u,v);
adde(v,u);
}
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
S[i].clear();
}
dfs1(1);
clo=0;
dfs2(1,1);
dfs3(1);
int q;
scanf("%d",&q);
while(q--){
int k,u,nc;
scanf("%d%d",&k,&u);
if(k==0){
scanf("%d",&nc);
modify(u,nc);
}else printf("%d\n",fen.sum(u));
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构