P2607 [ZJOI2008]騎士 基環樹,樹dp;
阿新 • • 發佈:2020-08-18
P2607 [ZJOI2008]騎士
本題本質上就是樹dp,和沒有上司的舞會差不多,只不過多了一個對基環樹的處理。
#include<iostream> #include<cstring> #include<string> #include<cstdio> #define ll long long using namespace std; const int maxn=2e6; int n,cnt; ll val[maxn]; int root; int head[maxn]; int vis[maxn]; ll fa[maxn]; ll f[maxn][2]; ll ans; int rot; ll max1(ll x,ll y){ if(x>=y){ return x; } return y; } inline ll read(){ ll ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-'){ f=-f; } ch=getchar(); } while(ch<='9'&&ch>='0'){ ret=ret*10+(ch^'0'); ch=getchar(); } return f*ret; } struct node{ int nex,to; }e[maxn]; void add(int u,int v){ cnt++; e[cnt].nex=head[u]; e[cnt].to=v; head[u]=cnt; } void dp(int x){ vis[x]=1;//打標記,用處是表明聯通塊,把該聯通塊進行標記 //以便遍歷整連線關係,除此之外並無用處 f[x][1]=val[x]; f[x][0]=0; for(int i=head[x];i;i=e[i].nex){ int to=e[i].to; if(to!=root){ dp(to); f[x][0]+=max1(f[to][0],f[to][1]); f[x][1]+=f[to][0]; } else{ f[to][1]=-maxn;//將環刪邊,定有一點不能選,故定此點不選 } } } void find(int x){ vis[x]=1; root=x; while(!vis[fa[root]]){//我們的目的是為了找環,而且該連通塊先前一定未遍歷過 //故該方法可以找出環來 root=fa[root]; vis[root]=1; } /* 該題中圖的特殊性 導致找環必須要從下到上 若從上到下 那麼如果起點為樹邊就無法找到環 該圖的根為環 */ dp(root); ll t; t=max(f[root][1],f[root][0]);//刪一條邊不選邊上一點的情況下,該聯通塊的最大值 root=fa[root]; dp(root); ans+=max1(t,max1(f[root][1],f[root][0])); //將兩種情況比較; } int main(){ n=read(); int x; for(int i=1;i<=n;i++){ val[i]=read(); x=read(); add(x,i); fa[i]=x; } for(int i=1;i<=n;i++){ if(!vis[i]){ find(i); } } cout<<ans; return 0; }
快去ac吧