[BZOJ] 1040: [ZJOI2008]骑士

 

最初想成了缩点了,感觉一个强连通分量只能选出一个人,gg...

 

正解基环树dp,把环断开后强制两个断点分别不选进行dp即可。

今天电脑抽风,老吞代码..

 

#include<iostream>
#include<cstring>
#include<cstdio>
#define int long long
using namespace std;

inline int rd() {
    int ret=0,f=1;
    char c;
    while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
    while(isdigit(c))ret=ret*10+c-'0',c=getchar();
    return ret*f;
}

const int MAXN=1000050;

int val[MAXN],wei[MAXN];

struct Edge {
    int from,next,to;
    Edge(int x=0,int y=0,int w=0) {from=x;next=y;to=w;}
} e[MAXN<<1];
int ecnt=1,head[MAXN];
inline void add(int x,int y){
    e[++ecnt]=Edge(x,head[x],y);
    head[x]=ecnt;
}

bool vis[MAXN];

int n;
int f[MAXN][2];
int cx,cy,ci;
void dfs(int x,int pre){
    f[x][1]=val[x];f[x][0]=0;
    for(int i=head[x];i;i=e[i].next){
        int v=e[i].to;
        if(v==pre||i==ci||(i==(ci^1))) continue;
        dfs(v,x);
        f[x][1]+=f[v][0];
        f[x][0]+=max(f[v][0],f[v][1]);
    }
}


void findCircle(int x,int pre){
    vis[x]=1;
    for(int i=head[x];i;i=e[i].next){
        int v=e[i].to;
        if(v==pre) continue;
        if(vis[v]) {cx=x,cy=v,ci=i;continue;}
        findCircle(v,x);
    }    
}

int ans;

signed main(){
    n=rd();
    int x,y,w;
    for(int i=1;i<=n;i++){
        val[i]=rd();x=rd();
        add(x,i);add(i,x);
    }
    int ans1=0,ans2=0;
    for(int i=1;i<=n;i++){
        if(vis[i]) continue;
        findCircle(i,0);
        dfs(cx,0);
        ans1=f[cx][0];
        dfs(cy,0);
        ans2=f[cy][0];
        ans+=max(ans1,ans2);
    }
    cout<<ans;
}

 

posted @ 2018-08-01 11:45  GhostCai  阅读(87)  评论(0编辑  收藏  举报