zoukankan      html  css  js  c++  java
  • ZJOI2008 骑士

    传送门

    这道题一开始看没什么头绪……之后觉得不妨把骑士向自己痛恨的那个人连一条边,那么我们好像就转化成了一个取父亲就不能取儿子这么一个操作。

    非常的像那个没有上司的舞会。

    不过这题有一些bug,就是在一些联通块中可能存在环。不过我们仔细想一下之后会发现,因为每个点的出度都是1,所以如果骑士之间的厌恶情况成了一个环,那么肯定是一个简单环,也就是一个连一个,最后头和尾相连的情况,否则就不满足每个点的出度都是1了。同样的,一个联通块中也不可能出现两个及以上的环。

    这样我们找到所有的联通块中的环,之后把环上的任意一条边断开,之后就肯定成为了一棵树。之后直接做树型DP即可。不过注意这里断边之后会出现两个新的点,第一种情况是要强制性选其中之一,第二种情况就强制性选另一个,直接在上面进行树型DP即可。

    因为图不一定是完全联通的,所以我们像tarjan一样枚举每一个点dfs,如果这个点被搜过,那么我们直接跳过。最后把所有联通块内部的答案加起来就是最终的结果。

    注意这里求一个联通块的环……因为后面DP方便我们还是选择了建立无向图。之后就跟着dalao学了一手怎么用位运算判断环……

    我们像跑网络流的时候一样,把ecnt的初始值设为-1,这样加入的两条边就可以通过^1来进行直接转移。之后在每次继续向下dfs的时候,把上次走的那条边也一起传下去,这样就可以如果当前的边^1是上一条边,就说明这条边已经走过了,那么直接继续。然后如果找到了环,我们记录一下两个端点的位置之后开始DP就可以了。

    然后在DP的时候,如果当前边或者其^1的值是你断过的那条边的边号,就直接继续。

    注意find的时候初始要传-2,因为-1^1 = -2.

    具体细节看一下代码。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    const int M = 1000005;
    ll n,s,tot,x1,x2,edg,dp[M][2],q[M],head[M],a,b,ans,ecnt = -1;
    bool vis[M];
    ll read()
    {
        ll ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
        if(ch == '-') op = -1;
        ch = getchar();
        }
        while(ch >='0' && ch <= '9')
        {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
        }
        return ans * op;
    }
    
    
    struct node
    {
        ll next,to;
    }e[M<<1];
    
    void add(ll x,ll y)
    {
        e[++ecnt].to = y;
        e[ecnt].next = head[x];
        head[x] = ecnt;
    }
    
    void find(ll x,ll pre)
    {
        vis[x] = 1;
        for(int i = head[x];i != -1;i = e[i].next)
        {
        if((i^1) == pre) continue;
        if(vis[e[i].to])
        {
            x1 = x,x2 = e[i].to,edg = i;
            continue;
        }
        find(e[i].to,i);
        }
    }
    
    void dfs(ll x,ll pre)
    {
        dp[x][0] = 0,dp[x][1] = q[x];
        for(int i = head[x];i != -1;i = e[i].next)
        {
        if((i^1) == pre) continue;
        if(i == edg || (i^1) == edg) continue;
        dfs(e[i].to,i);
        dp[x][1] += dp[e[i].to][0];
        dp[x][0] += max(dp[e[i].to][1],dp[e[i].to][0]);
        }
    }
    
    int main()
    {
        memset(head,-1,sizeof(head));
        n = read();
        rep(i,1,n) a = read(),b = read(),add(i,b),add(b,i),q[i] = a;
        ans = 0;
        rep(i,1,n)
        {
        if(vis[i]) continue;
        find(i,-2);
        dfs(x1,-1);
        ll cur = dp[x1][0];
        dfs(x2,-1);
        cur = max(cur,dp[x2][0]);
        ans += cur;
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    【转】C# 注册表简单操作
    [转]VC++之随父窗口变化调整控件大小/位置
    [转]vc 汉字汉语拼音首字母如何获取
    [转]浅析ActiveX控件的CAB压缩
    [转]VC6.0中使用MFC开发ActiveX及简单验证
    ElasticSearch增加索引字段
    单词缩写
    linux命令
    使用PR插件Twixtor Pro对视频补帧
    Linux创建脚本服务
  • 原文地址:https://www.cnblogs.com/captain1/p/9592167.html
Copyright © 2011-2022 走看看