[BZOJ 1040] 骑士
Link:
Solution:
基环树$dp$
如果仅仅是一棵树,直接树形$dp$即可,维护选与不选两种状态下的方案数
但此题是一个基环树,即除了一个环外是一个树形结构
对于环,一般都是将环转化为链处理
我们只需要删掉环上的任意一条边即可将环转化为树,那我们只需要人为判断这条边对答案的贡献就行了
设这条边是$(u,v)$,那么有2种情况
1.不选$u$,那么$v$选不选都行,以$u$为根跑一遍树形$dp$
2.不选$v$,那么$u$选不选都行,以$v$为根跑一遍树形$dp$
建图有两种方式:
Solution A:
先将原图建好,$dfs$找到返祖边,删除返祖边
Solution B:
在连边时用并查集维护,如果出现环则不连这条边
Solution B 跑得更快些
Code:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=1e6+10; bool vis[MAXN],flag=false; int n,l,r,dat[MAXN],mat[MAXN]; ll dp[MAXN][2],res=0; vector<int> G[MAXN]; void dfs(int x,int anc) { vis[x]=true; for(int i=0;i<G[x].size() && !flag;i++) { int v=G[x][i]; if(v==anc) continue; if(vis[v]) { flag=true; G[x].erase(find(G[x].begin(),G[x].end(),v)); G[v].erase(find(G[v].begin(),G[v].end(),x)); l=x;r=v;break; } dfs(v,x); } } void Trdp(int x,int anc,int ban) { vis[x]=true; if(x==ban) dp[x][1]=0; else dp[x][1]=dat[x]; dp[x][0]=0; for(int i=0;i<G[x].size();i++) { int v=G[x][i]; if(v==anc) continue; Trdp(v,x,ban); dp[x][0]+=max(dp[v][0],dp[v][1]); dp[x][1]+=dp[v][0]; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d",&dat[i],&mat[i]); G[i].push_back(mat[i]);G[mat[i]].push_back(i); } for(int i=1;i<=n;i++) { if(vis[i]) continue; ll t=0;flag=false;dfs(i,0); Trdp(l,0,r);t=max(dp[l][0],dp[l][1]); Trdp(r,0,l);t=max(t,max(dp[r][0],dp[r][1])); res+=t; } printf("%lld",res); return 0; }
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=1e6+10; vector<int> G[MAXN]; ll dp[MAXN][2],res=0,t=0; int f[MAXN],n,x,dat[MAXN],l[MAXN],r[MAXN],cnt=0; int find(int x){return (x==f[x])?x:(f[x]=find(f[x]));} void Trdp(int x,int anc) { dp[x][0]=0;dp[x][1]=dat[x]; for(int i=0;i<G[x].size();i++) { int v=G[x][i]; if(v==anc) continue; Trdp(v,x); dp[x][0]+=max(dp[v][1],dp[v][0]); dp[x][1]+=dp[v][0]; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=n;i++) { scanf("%d%d",&dat[i],&x); if(find(i)!=find(x)) { G[x].push_back(i);G[i].push_back(x); f[f[i]]=f[x]; } else l[++cnt]=i,r[cnt]=x; } for(int i=1;i<=cnt;i++) { Trdp(l[i],0);t=dp[l[i]][0]; Trdp(r[i],0);t=max(t,dp[r[i]][0]); res+=t; } printf("%lld",res); return 0; }
Review:
1、处理环的常用方式:将环转化为链
2、用并查集维护点集的方式寻找环的效率更高