luogu2607/bzoj1040 [ZJOI2008]骑士 (基环树形dp)

Posted ressed

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu2607/bzoj1040 [ZJOI2008]骑士 (基环树形dp)相关的知识,希望对你有一定的参考价值。

N个点,每个点发出一条边,那么这个图的形状一定是一个基环树森林(如果有重边就会出现森林)

那我做f[0][x]和f[1][x]分别表示对于x子树,x这个点选还是不选所带来的最大价值

然后就变成了这好几个环上不能选相邻的点,最大的价值和

我们把这个环从N到1处断开,然后钦定一下1选还是不选,统计一下答案就可以了。

 

  1 #include<bits/stdc++.h>
  2 #define pa pair<int,int>
  3 #define ll long long
  4 using namespace std;
  5 const int maxn=1000010;
  6 
  7 inline ll rd(){
  8     ll x=0;char c=getchar();int neg=1;
  9     while(c<0||c>9){if(c==-) neg=-1;c=getchar();}
 10     while(c>=0&&c<=9) x=x*10+c-0,c=getchar();
 11     return x*neg;
 12 }
 13 
 14 int N;
 15 int eg[maxn*2][2],egh[maxn],ect;
 16 int dep[maxn],fa[maxn];
 17 int root[maxn][2],pct,rh[maxn],rct;
 18 int stk[maxn],top[maxn];
 19 ll f[2][maxn];
 20 bool flag[maxn],isroot[maxn],connected[maxn];
 21 
 22 inline void adeg(int a,int b){
 23     eg[++ect][0]=b;eg[ect][1]=egh[a];egh[a]=ect;
 24 }
 25 inline void adrot(int a,int b){
 26     root[++rct][0]=b;root[rct][1]=rh[a];rh[a]=rct;
 27 }
 28 
 29 void dfs1(int ii,int x){
 30     flag[x]=1;
 31     //printf("%d %d %d
",x,fa[x],dep[x]);
 32     for(int i=egh[x];i!=-1;i=eg[i][1]){
 33         int b=eg[i][0];if(b==fa[x]) continue;
 34         //printf("#%d %d %d %d %d
",x,b,flag[b],i,eg[i][1]);
 35         if(flag[b]){
 36             if(connected[ii]) continue;
 37             int u=x,v=b,lca,cnt=0;
 38             if(dep[u]<dep[v]) swap(u,v);
 39             while(dep[u]!=dep[v]) adrot(ii,u),isroot[u]=1,u=fa[u];
 40             while(u!=v){
 41                 isroot[u]=isroot[v]=1;
 42                 adrot(ii,u);
 43                 stk[++cnt]=v;
 44                 u=fa[u];v=fa[v];
 45             }lca=u;isroot[lca]=1;adrot(ii,lca);
 46             for(int j=cnt;j;j--) adrot(ii,stk[j]);
 47             connected[ii]=1;
 48         }else{
 49             dep[b]=dep[x]+1;fa[b]=x;
 50             dfs1(ii,b);
 51         }
 52     }
 53 }
 54 
 55 void dfs2(int x,int F){
 56     for(int i=egh[x];i!=-1;i=eg[i][1]){
 57         int b=eg[i][0];if(b==F||isroot[b]) continue;
 58         dfs2(b,x);
 59         f[0][x]+=max(f[0][b],f[1][b]);
 60         f[1][x]+=f[0][b];
 61     }
 62 }
 63 
 64 inline ll solve(int p){
 65     if(rh[p]==-1) return max(f[0][top[p]],f[1][top[p]]);
 66     ll re=0;
 67     ll g1=f[1][root[rh[p]][0]],g0=0;
 68     for(int i=root[rh[p]][1];i!=-1;i=root[i][1]){
 69         ll xx=max(g0,g1);
 70         g1=g0+f[1][root[i][0]];
 71         g0=xx+f[0][root[i][0]];
 72     }re=max(re,g0);
 73     
 74     g1=0,g0=f[0][root[rh[p]][0]];
 75     for(int i=root[rh[p]][1];i!=-1;i=root[i][1]){
 76         ll xx=max(g0,g1);
 77         g1=g0+f[1][root[i][0]];
 78         g0=xx+f[0][root[i][0]];
 79     }re=max(re,max(g0,g1));
 80     return re;
 81 }
 82 
 83 int main(){
 84     int i,j,k;
 85     //freopen("2607.in","r",stdin);
 86     N=rd();memset(egh,-1,sizeof(egh));
 87     for(i=1;i<=N;i++){
 88         int a=rd(),b=rd();
 89         f[1][i]=a;
 90         adeg(i,b);adeg(b,i);
 91     }memset(rh,-1,sizeof(rh));
 92     for(i=1;i<=N;i++){
 93         if(!flag[i]) top[++pct]=i,dfs1(pct,i);
 94     }
 95     //for(i=1;i<=rct;i++) printf("!%d %d %d
",i,root[i][0],root[i][1]);
 96     for(i=1;i<=rct;i++) dfs2(root[i][0],0);
 97     ll ans=0;
 98     for(i=1;i<=pct;i++){
 99         ans+=solve(i);
100     }
101     printf("%lld
",ans);
102     
103     return 0;
104 }

 

以上是关于luogu2607/bzoj1040 [ZJOI2008]骑士 (基环树形dp)的主要内容,如果未能解决你的问题,请参考以下文章

luogu1040加分二叉树

luogu1040 加分二叉树

树形dp入门-加分二叉树(luogu1040)

P1040 加分二叉树

洛谷1040 加分二叉树 区间dp

P1040 加分二叉树