ZJOI2008 骑士 【基环树】
ZJOI2008 骑士
题意见链接。。。
本题较为经典,值得一做。基环外向树练手好题。
如果不考虑环的情况,则就是普通树形DP,f[i][0/1] 表示这个点取或不取。
对于此题一棵树只能出现一个环,我们任意删去环上的一条边,即可转环为树。
对于 (u,v)
1:u 不取,则以 u 为根DP。
2:v 不取,则以 v 为根DP。
不需要考虑两个点的分别情况,DP的时候考虑在内。
判环我用并查集。
贴代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 int n,m,cnt; 5 const int N=1000005; 6 int fa[N],to[N<<1],nxt[N<<1],hea[N],ra[N],rb[N]; 7 ll f[N],g[N],v[N],ans; 8 inline int getf(int x) 9 { 10 return fa[x]==x?x:fa[x]=getf(fa[x]); 11 } 12 inline void dfs(int x,int fat) 13 { 14 f[x]=v[x],g[x]=0; 15 for (int i=hea[x]; i; i=nxt[i]) 16 { 17 int y=to[i]; 18 if (y==fat) continue; 19 dfs(y,x); 20 g[x]+=max(f[y],g[y]); 21 f[x]+=g[y]; 22 } 23 } 24 inline void add(int x,int y) 25 { 26 to[++cnt]=y,nxt[cnt]=hea[x],hea[x]=cnt; 27 } 28 int main() 29 { 30 scanf("%d",&n); 31 for (int i=1; i<=n; ++i) fa[i]=i; 32 memset(hea,-1,sizeof(hea)); 33 int b; 34 for (int i=1; i<=n; ++i) 35 { 36 scanf("%lld%d",&v[i],&b); 37 if (getf(i)!=getf(b)) 38 { 39 add(i,b); add(b,i); 40 fa[fa[i]]=fa[b]; 41 } 42 else ra[++m]=i,rb[m]=b; 43 } 44 ll t; 45 for (int i=1; i<=m; ++i) 46 { 47 dfs(ra[i],0); t=g[ra[i]]; 48 dfs(rb[i],0); t=max(t,g[rb[i]]); 49 ans+=t; 50 } 51 printf("%lld\n",ans); 52 return 0; 53 }
fighting fighting fighting