[ZJOI2008]骑士

题目链接

这题其实就是没有上司的舞会在基环树上的扩展。

那么做法也差不多,对于基环树,在环上任选一条边删掉。

由于这条边连接的两个点不能同时选,于是硬点一个点不选,以另一个点为根做一遍dp,这样做两次dp之后取个最大值就是答案。

这题有可能是个森林,于是对于每个基环树做一遍加起来就行了。

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
#define maxn 2000050

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar((x%10)^48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}

const int mod = 1e9+7;

int n,m,f[2][maxn][2],head[maxn],tot,v[maxn],vis[maxn],RT,ff[maxn],rt;
struct edge {int to,nxt;}e[maxn<<1];

void ins(int u,int v) {e[++tot].to=v,e[tot].nxt=head[u],head[u]=tot;}

void dp(int k,int x,int pre) {
    f[k][x][1]=v[x];vis[x]=1;
    for(int i=head[x];i;i=e[i].nxt) {
        if(e[i].to==pre) {f[k][e[i].to][1]=-1e9;continue;}
        dp(k,e[i].to,pre);
        f[k][x][0]+=max(f[k][e[i].to][1],f[k][e[i].to][0]);
        f[k][x][1]+=f[k][e[i].to][0];
    }
}

int calc(int x) {
    while(!vis[x]) vis[x]=1,x=ff[x];
    dp(0,x,x);dp(1,ff[x],ff[x]);
    //for(int i=1;i<=n;i++) printf("%lld %lld
",f[1][i][0],f[1][i][1]);
    return max(max(f[0][x][1],f[0][x][0]),max(f[1][ff[x]][0],f[1][ff[x]][1]));
}

void solve() {
    read(n);int x,ans=0;
    for(int i=1;i<=n;i++) read(v[i]),read(x),ins(x,i),ff[i]=x;
    for(int i=1;i<=n;i++) if(!vis[i]) ans+=calc(i);
    write(ans);
}

signed main() {solve();return 0;}

原文地址:https://www.cnblogs.com/hbyer/p/9862106.html