[BZOJ1040][ZJOI2008]骑士 基环树DP
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1040
题目给出了$n$个点和$n$条无向边,即一棵基环树或者基环树森林。
如果题目给的关系是在一棵树上,就是一道经典的树形DP。现在我们考虑转化一下。
我们先找到那个环上的任意一条边,端点为u,v。加上这条边的影响仅仅是不能同时选择u和v。
所以我们考虑去掉这条边再分类讨论。如果不选u,那么v任意选;如果不选v,那么u任意选。那么DP的时候强制不走这条边,同时取两种不选根节点的最大值作为答案就行了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 int inline readint(){ 7 int Num;char ch; 8 while((ch=getchar())<'0'||ch>'9');Num=ch-'0'; 9 while((ch=getchar())>='0'&&ch<='9') Num=Num*10+ch-'0'; 10 return Num; 11 } 12 int N,val[1000010]; 13 int to[2000010],ne[2000010],fir[1000010],cnt=-1; 14 void add(int a,int b){ 15 to[++cnt]=b; 16 ne[cnt]=fir[a]; 17 fir[a]=cnt; 18 } 19 bool vis[1000010]; 20 int U,V,E; 21 void Dfs(int x,int fa){ 22 vis[x]=true; 23 for(int i=fir[x];i!=-1;i=ne[i]){ 24 int v=to[i]; 25 if(v!=fa){ 26 if(vis[v]){ 27 U=x; 28 V=v; 29 E=i; 30 } 31 else Dfs(v,x); 32 } 33 } 34 } 35 ll f[1000010],g[1000010]; 36 void Dp(int x,int fa){ 37 f[x]=val[x]; 38 g[x]=0; 39 for(int i=fir[x];i!=-1;i=ne[i]){ 40 int v=to[i]; 41 if(i==E||(i^1)==E||v==fa) continue; 42 Dp(v,x); 43 f[x]+=g[v]; 44 g[x]+=max(f[v],g[v]); 45 } 46 } 47 int main(){ 48 memset(fir,-1,sizeof(fir)); 49 N=readint(); 50 for(int i=1;i<=N;i++){ 51 val[i]=readint(); 52 int tmp=readint(); 53 add(i,tmp); 54 add(tmp,i); 55 } 56 ll ans=0; 57 for(int i=1;i<=N;i++){ 58 if(!vis[i]){ 59 Dfs(i,0); 60 Dp(U,0); 61 ll tmp=g[U]; 62 Dp(V,0); 63 tmp=max(tmp,g[V]); 64 ans+=tmp; 65 } 66 } 67 printf("%lld\n",ans); 68 return 0; 69 }