D28 基环树 树形DP P2607 [ZJOI2008] 骑士
视频链接:D28 基环树 P2607 [ZJOI2008] 骑士_哔哩哔哩_bilibili
基环树上选点问题:断开任一环边,对两颗树DP,取最大。
1. n个点 n条边,构成多颗外向基环树
2. 深搜找环上两个邻点 r1,r2
3. 分别以 r1,r2 为根做树形DP(没有上司的舞会)
4. sum+=max(s1,s2)
// 外向基环树 树形DP O(n) #include<bits/stdc++.h> using namespace std; #define int long long const int N=1000010; int n,w[N]; vector<int> e[N]; int r1,r2,vis[N],f[N][2],sum; void dfs(int u,int rt){ vis[u]=1; for(int v:e[u]){ if(v==rt){r1=u,r2=v;return;} if(!vis[v]) dfs(v,rt); } } int DP(int u,int rt){ f[u][0]=0; f[u][1]=w[u]; for(int v:e[u])if(v!=rt){ DP(v,rt); f[u][0]+=max(f[v][0],f[v][1]); f[u][1]+=f[v][0]; } return f[u][0]; //保证r1,r2不会同时选 } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n; for(int i=1,j;i<=n;i++){ cin>>w[i]>>j; e[j].push_back(i); //多人厌恶j,建成外向基环树 } for(int i=1;i<=n;i++){ if(!vis[i]){ r1=r2=0; dfs(i,i); if(r1) sum+=max(DP(r1,r1),DP(r2,r2)); } } cout<<sum; }
// 无向基环树 树形DP O(n) #include<bits/stdc++.h> using namespace std; #define int long long const int N=1000010; int n,w[N],r1,r2,vis[N],be[N<<1]; int h[N],to[N<<1],ne[N<<1],idx=1; void add(int a,int b){ to[++idx]=b,ne[idx]=h[a],h[a]=idx; } int f[N][2],sum; void dfs(int u,int ine){ vis[u]=1; for(int i=h[u];i;i=ne[i]){ if(i==(ine^1)) continue; int v=to[i]; if(!vis[v]) dfs(v,i); else{ r1=u,r2=v,be[i]=1,be[i^1]=1; //遇到环 } } } int DP(int u,int ine){ f[u][0]=0; f[u][1]=w[u]; for(int i=h[u];i;i=ne[i]){ if(i==(ine^1)||be[i]) continue; int v=to[i]; DP(v,i); f[u][0]+=max(f[v][0],f[v][1]); f[u][1]+=f[v][0]; } return f[u][0]; } signed main(){ scanf("%lld",&n); for(int v=1,u;v<=n;v++){ scanf("%lld%lld",&w[v],&u); add(u,v);add(v,u); //建无向基环树 } for(int i=1;i<=n;i++){ if(!vis[i]){ r1=r2=0; dfs(i,0); if(r1)sum+=max(DP(r1,0),DP(r2,0)); } } printf("%lld",sum); }
// 无向基环树 并查集+树形DP O(n) #include<bits/stdc++.h> using namespace std; #define int long long const int N=1000010; int n,w[N]; int fa[N],f[N][2],sum; vector<int> e[N]; vector<pair<int,int>> roots; int find(int x){ if(fa[x]==x) return x; return fa[x]=find(fa[x]); } int DP(int u,int ff){ f[u][0]=0; f[u][1]=w[u]; for(int v:e[u])if(v!=ff){ DP(v,u); f[u][0]+=max(f[v][0],f[v][1]); f[u][1]+=f[v][0]; } return f[u][0]; } signed main(){ scanf("%d",&n); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1,j;i<=n;i++){ scanf("%d%d",&w[i],&j); int a=find(i),b=find(j); if(a!=b){ //并查集维护有向关系,未构成环时,合并,建无向树 fa[a]=b; e[i].push_back(j),e[j].push_back(i); } else roots.push_back({i,j}); //构成环时,记录环上两个邻点 } for(auto [r1,r2]:roots) sum+=max(DP(r1,0),DP(r2,0)); printf("%lld",sum); }
浙公网安备 33010602011771号