1156 : 彩色的树
描述
给定一棵n个节点的树,节点编号为1, 2, …, n。树中有n - 1条边,任意两个节点间恰好有一条路径。这是一棵彩色的树,每个节点恰好可以染一种颜色。初始时,所有节点的颜色都为0。现在需要实现两种操作:
1. 改变节点x的颜色为y;
2. 询问整棵树被划分成了多少棵颜色相同的子树。即每棵子树内的节点颜色都相同,而相邻子树的颜色不同。
输入
第一行一个整数T,表示数据组数,以下是T组数据。
每组数据第一行是n,表示树的节点个数。接下来n - 1行每行两个数i和j,表示节点i和j间有一条边。接下来是一个数q,表示操作数。之后q行,每行表示以下两种操作之一:
1. 若为"1",则询问划分的子树个数。
2. 若为"2 x y",则将节点x的颜色改为y。
输出
每组数据的第一行为"Case #X:",X为测试数据编号,从1开始。
接下来的每一行,对于每一个询问,输出一个整数,为划分成的子树个数。
数据范围
1 ≤ T ≤ 20
0 ≤ y ≤ 100000
小数据
1 ≤ n, q ≤ 5000
大数据
1 ≤ n, q ≤ 100000
- 样例输入
-
2 3 1 2 2 3 3 1 2 2 1 1 5 1 2 2 3 2 4 2 5 4 1 2 2 1 2 3 2 1
- 样例输出
-
Case #1: 1 3 Case #2: 1 5
由于无环,和断点(改变颜色的点)连接的点如果颜色和改变前一样,树++。和改变后一样树--。
代码一号,树上乱搞:
#include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn=220010; int Laxt[maxn],Next[maxn],To[maxn],cnt; int ans,V[maxn];void add(int u,int v) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } void init() { ans=1;cnt=0; memset(Laxt,0,sizeof(Laxt)); memset(V,0,sizeof(V)); } void change(int x,int y) { bool flag=false; int tmp=0; for(int i=Laxt[x];i;i=Next[i]){ if(V[To[i]]==y) ans--; if(V[To[i]]==V[x]) ans++; } V[x]=y; } int main() { int T,n,i,j,x,y,q,u,v,Case=0; scanf("%d",&T); while(T--){ printf("Case #%d:\n",++Case); init(); scanf("%d",&n); for(i=1;i<n;i++){ scanf("%d%d",&u,&v); add(u,v); add(v,u); } scanf("%d",&q); while(q--){ scanf("%d",&x); if(x==1) printf("%d\n",ans); else { scanf("%d%d",&x,&y); change(x,y); } } } return 0; }
每次访问邻边导致耗时过多。怎么记录?
首先,无向图变有向图,==其实就是父子关系辣。
一个节点记录相邻儿子的信息。如果改变某点,就只改变它父亲的信息就ok。
强势之处:
通过父子关系使记录和查询变成O(1),访问效率高;
map记录每个节点的有效颜色,空间利用效率高。
代码二号:
#include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cstring> #include<map> using namespace std; const int maxn=110010; int Laxt[maxn],Next[maxn<<1],To[maxn<<1],cnt; int ans,V[maxn],fa[maxn]; map<int,int>G[maxn]; void add(int u,int v) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } void init() { ans=1;cnt=0; memset(Laxt,0,sizeof(Laxt)); memset(V,0,sizeof(V)); } void change(int x,int y) { if(V[x]==y) return ; ans+=G[x][V[x]]; ans-=G[x][y]; if(fa[x]!=0){ if(V[fa[x]]==V[x]) ans++; if(V[fa[x]]==y) ans--; G[fa[x]][V[x]]--; G[fa[x]][y]++; } V[x]=y; } void dfs(int u,int pre) { fa[u]=pre; for(int i=Laxt[u];i;i=Next[i]){ if(To[i]==pre) continue; G[u][0]++; dfs(To[i],u); } } int main() { int T,n,i,x,y,q,Case=0; scanf("%d",&T); while(T--){ printf("Case #%d:\n",++Case); init(); scanf("%d",&n); for(i=1;i<=n;i++) G[i].clear(); for(i=1;i<n;i++){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(1,0); scanf("%d",&q); while(q--){ scanf("%d",&x); if(x==1) printf("%d\n",ans); else { scanf("%d%d",&x,&y); change(x,y); } } } return 0; }