BZOJ1040[ZJOI2008]骑士
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 8 typedef long long ll; 9 const int N=1000005,M=2000005; 10 11 int n,tot,U,V,E,now[N],prep[M],son[M],val[N]; 12 ll ans,f[N],g[N]; 13 bool vis[N]; 14 15 int read(){ 16 static int x,f; static char ch; x=0,f=1; 17 for (ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') f=-1; 18 for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; 19 } 20 21 void link(int x,int y){prep[++tot]=now[x],now[x]=tot,son[tot]=y;} 22 23 void dfs(int x,int y){ 24 vis[x]=1; 25 for (int i=now[x],so=son[i];i!=-1;i=prep[i],so=son[i]){ 26 if (so!=y){ 27 if (vis[so]){ 28 U=x,V=so,E=i; 29 continue; 30 }else dfs(so,x); 31 } 32 } 33 } 34 35 void tree_dp(int x,int y,int ban){ 36 f[x]=val[x],g[x]=0; 37 for (int i=now[x],so=son[i];i!=-1;i=prep[i],so=son[i]){ 38 if (so!=y&&i!=ban&&(i^1)!=ban){ 39 tree_dp(so,x,ban); 40 f[x]+=g[so],g[x]+=max(f[so],g[so]); 41 } 42 } 43 } 44 45 int main(){ 46 n=read(); tot=-1; 47 memset(now,-1,sizeof(now)); 48 for (int x,i=1;i<=n;i++){ 49 val[i]=read(),x=read(); 50 link(x,i),link(i,x); 51 } 52 ans=0; 53 for (int i=1;i<=n;i++){ 54 if (!vis[i]){ 55 dfs(i,0); 56 tree_dp(U,0,E); 57 ll x=g[U]; 58 tree_dp(V,0,E); 59 ans+=max(x,g[V]); 60 } 61 } 62 printf("%lld\n",ans); 63 return 0; 64 }
题目大意:给定一个基环外向森林,点带权,求最大点独立集。n<=10^6.
做法:基环树dp,设(u,v)为环上的一条边,f[x]表示以x为根的子树中选x的最大权和,g[x]不选。
考虑如下步骤:
1:将这条边断开,变为树。
2:u,v必有一个点不选,考虑分别以u,v为根做一次dp,然后用max(g[u],g[v])更新答案。
细节:(u,v)怎么找? dfs一次,如果x的出边son之前访问过了,那么(x,son)就是换上的一条边,记录一下即可。