1、题目描述:题
2、第一次写没考虑复杂度问题,仅是直接入手求解,用到递归,设置数组记忆访问过的节点,及时剪枝。。。虽然剪枝意义不大。此外,如果由一个无根树转化为一个以1为节点的有根树的话,在搜索时会节省很多时间。
以下是自己的代码
#include <iostream>
#include<cstring>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int s[100001];
int l[100001][100001];
bool r[100001];
bool cha(int s[],int i,int n){
if(r[i])return true;
bool flag=true;
for(int k=i+1;k<n;i++){
if(l[i][k]==1){
if(s[i]==s[k]){
flag =flag&cha(s,k,n);
}
else{
flag=false;
break;
}
}
}
s[i]=flag;
return flag;
}
int main(int argc, char** argv) {
int t;
cin>>t;
while(t--){
memset(s,0,sizeof(s));
memset(l,0,sizeof(l));
int n=0;cin>>n;
for(int i=0;i<n;i++){
int x,y;
cin>>x>>y;
l[x][y]=1;
}
int c;
cin>>c;
while(c--){
int x;
cin>>x;
switch(x){
case 1:{int count=0;
for(int i=1;i<n;i++){
if(cha(s,i,n))count++;
}
cout<<count;
break;
}
case 2:{
int a,b;
cin>>a>>b;
s[a]=b;
break;
}
}
}
}
return 0;
}
恒姐的思路:
解题思路:今天的比赛用dfs只过了小数据==。当这棵树退化为一条链时,肯定会TLE了。下面来学习一下AC的代码的思路。
首先把图建立起来,然后以1为根转化为有根树,在转化的时候,统计所有结点不同颜色的子结点的个数。由于颜色可能会比较多,这里可以使用map来存储每个结点的不同颜色的个数。下面我们来思考一下改变结点i的颜色会带来哪些变化。
首先思考一下怎样才会产生新的子树。(1)如果结点i是一个叶子结点,当所有叶子结点均为颜色0时,而把结点i修改为颜色1,自然会多产生一棵子树。我们可以这样来计算这棵新的树:修改前颜色0的叶子有num个,修改后自然会有num–。那么前后的差值就是新的子树的个数。这是子树的第一个来源。(2)如果结点i是一个中间的结点,假设它的颜色为0,且它有num个颜色为0的子结点。如果把结点i的颜色修改为1,自然我们知道新产生了num棵子树。此时我们也可以通过对比前后的不同得到这个值:修改前结点i的子结点中颜色为0的有cs[i][0]个(cs[i][j]表示结点i的颜色为j的子结点的个数,即num==cs[i][0]),将结点i颜色修改为1后,cs[i][1]为0,那么新产生的子树就是cs[i][0]-cs[i][1]。这是子树的第二个来源。
综上,我们发现,新的子树总是可以通过前后结点i的子结点数的变化得到。不过对于第二种情况,还要注意修改fa[i]对应的子结点情况。因为修改了i的颜色影响的是fa[i]结点的情况。
本题值得学习的地方:(1)提前写好树和图的存储模板,包括加边,记录父亲,记录子结点数等基本操作。方便后续处理。(2)注意观察执行某个操作后的变与不变。通过数学的推导来计算结果往往效率比较高。
[cpp] view plaincopyprint?
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;
#define me(x) memset(x,0,sizeof(x))
#define sy system("pause")
#define maxn 100005
using namespace std;
struct edge
{
int to, nx;
};
edge es[maxn * 2];//边集
int st[maxn], en;//en表示边的个数,st[x]是链表的头结点
map<int, int> cs[maxn];//cs[i][j]表示结点i为根且颜色为j的儿子结点的个数
int n, q, ans;
int fa[maxn], color[maxn];//fa[i]表示结点i的父结点,color[i]表示结点i的颜色
void d__add(int x, int y)
{
edge e;
e.to = y;
e.nx = st[x];
es[++en] = e;
st[x] = en;
}
void add(int x, int y)//加边操作
{
d__add(x, y);
d__add(y, x);
}
void dfs(int x)//无根树转化为以x为根的有根树
{
int i, tot = 0;
for (i = st[x]; i; i = es[i].nx)
if (es[i].to != fa[x])
{
fa[es[i].to] = x;
tot++;
dfs(es[i].to);
}
cs[x][0] = tot;//儿子结点的个数
}
void change(int x, int y)//将结点x的颜色修改为y
{
if (color[x] == color[fa[x]]) ans++;//假设修改后父子结点的颜色会不同,预先加1
ans += cs[x][color[x]];//先加上所有原来颜色的儿子结点的个数
if (fa[x])//如果x的父结点存在,更新fa[x]的子结点情况
{
cs[fa[x]][color[x]]--;//父结点的子结点中颜色为color[x]的减少一个
cs[fa[x]][y]++;//颜色为y的增加一个
}
color[x] = y;//修改颜色
if (color[x] == color[fa[x]]) ans--;//如果修改后的颜色和父结点的颜色一致,结果减一
ans -= cs[x][color[x]];///减去所有目前颜色的儿子结点的个数
}
void solve(int cas)
{
int i, a, b;
scanf("%d", &n);
me(st); en = 0;
for (i = 1; i<n; i++)
{
scanf("%d%d", &a, &b);
add(a, b);
}
for (i = 1; i <= n; i++) cs[i].clear();
fa[1] = 0; me(color); color[0] = -10000097;
dfs(1); ans = 1;
scanf("%d", &q);
printf("Case #%d:\n", cas);
for (i = 0; i<q; i++)
{
scanf("%d", &a);
if (a == 1) printf("%d\n", ans);
else
{
scanf("%d%d", &a, &b);
change(a, b);
}
}
}
int main()
{
//freopen("t.txt", "r", stdin);
int T, i;
scanf("%d", &T);
for (i = 1; i <= T; i++) solve(i);
return 0;
}