您的位置:首页 > 编程语言

编程之美2015初赛第一场 hihoCoder #1156 : 彩色的树(染色问题)

2015-08-17 20:27 381 查看

#1156 : 彩色的树

时间限制:2000ms
单点时限:1000ms
内存限制:256MB


描述

给定一棵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


解题步骤:1、求出所有点的父结点,这边用的bfs遍历

2、使用一个map<int,int>mp
,mp[i][j]=k表示第i个结点的颜色是j,并且和它同颜色的儿子结点数为k


3、ans开始只有1

4、color数组初始化为0

5、如果要更新颜色,求出变化的子树数量,更新ans

6、至于子树数量的变化,如x的颜色变为y,那么先求出x颜色没变化时的子结点ans0,再求出颜色变化后的子结点ans1,然后ans=ans+ans0-ans1,更新ans

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
using namespace std;
#define N 100006
int n;
vector<int> edge
;//保存边
int fa
;//记录父节点
map<int,int>mp
;//mp[i][j]=k表示第i个结点的颜色是j,并且和它同颜色的儿子结点数为k
int color
;//color用来记录结点的颜色,初始化为0
void bfs()
{
queue<int> q;
q.push(1);
fa[1]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0;i<edge[u].size();i++)
{
int v=edge[u][i];
if(fa[v]!=-1) continue;
fa[v]=u;
q.push(v);
}
}
}
int main()
{
int t;
scanf("%d",&t);
int ac=0;
while(t--)
{
printf("Case #%d:\n",++ac);
for(int i=0;i<N;i++)edge[i].clear();
memset(fa,-1,sizeof(fa));

scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}

bfs();//bfs用来求所有的点的父节点,待会要用到

for(int i=1;i<=n;i++)
{
mp[i].clear();
color[i]=0;
if(i==1) mp[i][0]=edge[i].size();
else mp[i][0]=edge[i].size()-1;//这边减掉1的原因是i肯定要和1这个根结点相连,所以减1
}
//for(int i=1;i<=n;i++)
//printf("--%d\n",mp[i][0]);

int q;
scanf("%d",&q);
int ans=1;
while(q--)
{
int c;
scanf("%d",&c);
if(c==1) printf("%d\n",ans);//如果是1就输出结果,开始只有一棵
else
{
int x,y;
scanf("%d%d",&x,&y);
int ans0=mp[x][color[x]];//ans0表示还未变色时x的子结点总数
if(x!=1 && color[fa[x]]==color[x]) ans0++;//如果不是根结点,并且x的父结点和它颜色相同,则++

int ans1=mp[x][y];//ans1表示x变完颜色后的子结点个数
if(x!=1 && color[fa[x]]==y) ans1++;//如果不是根结点,并且x的父结点和它颜色相同,则++

ans=ans+ans0-ans1;//ans加上增加的子树

if(x!=1)
{
mp[fa[x]][color[x]]--;//因为要变色,所以父结点的子结点 -1
color[x]=y;//将颜色改变
mp[fa[x]][color[x]]++;//当前颜色的子结点 +1
}
color[x]=y;//如果是根节点的话,
}
}
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: