2015 编程之美彩色的树A

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;  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值