D28 基环树 树形DP P2607 [ZJOI2008] 骑士

视频链接:D28 基环树 P2607 [ZJOI2008] 骑士_哔哩哔哩_bilibili

P2607 [ZJOI2008] 骑士 - 洛谷

基环树上选点问题:断开任一环边,对两颗树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);
}

 

posted @ 2022-07-10 23:33  董晓  阅读(716)  评论(0)    收藏  举报