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('\n')
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\n",ans);
    return 0;
}

 

posted @ 2018-09-05 15:01  CaptainLi  阅读(104)  评论(0编辑  收藏  举报