bzoj1040[ZJOI2008]骑士
题意:
n个骑士,每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),且有一个战斗力。求从所有的骑士中选出一个骑士之间没有矛盾的骑士军团最大战斗力之和。n最大10e6
题解:
厌恶关系实际上是无向的。从每个骑士出发,沿着关系走可以得一个基环树(就是只有一个环,且断了环就会变成一棵树的连通块)。于是先dfs找环,然后断环,分别尝试以去掉的那条边的两个节点u,v为根,作树形dp,将f[u][0]与f[v][0]的最大值计入答案。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 1000010 6 #define ll long long 7 using namespace std; 8 9 bool vis[maxn]; ll f[maxn][2],w[maxn]; int n,bad; 10 struct e{int f,t,n;}; e es[maxn*2]; int ess,g[maxn]; 11 void pe(int f,int t){ 12 es[++ess]=(e){f,t,g[f]}; g[f]=ess; es[++ess]=(e){t,f,g[t]}; g[t]=ess; 13 } 14 void dfs(int x,int fa){ 15 vis[x]=1; 16 for(int i=g[x];i!=-1;i=es[i].n)if(i!=fa){ 17 if(vis[es[i].t])bad=i;else dfs(es[i].t,i^1); 18 } 19 } 20 ll dp(int x,int fa,int b){ 21 if(f[x][b]!=-1)return f[x][b]; f[x][b]=0; //printf("%d %d %d %d %d\n",x,fa,b,f[x][b]); 22 for(int i=g[x];i!=-1;i=es[i].n)if(i!=fa&&i!=bad&&i!=(bad^1)){ 23 if(!b)f[x][b]+=max(dp(es[i].t,i^1,0),dp(es[i].t,i^1,1)+w[es[i].t]); 24 else f[x][b]+=dp(es[i].t,i^1,0); 25 } 26 return f[x][b]; 27 } 28 int main(){ 29 scanf("%d",&n); memset(vis,0,sizeof(vis)); ll ans=0; memset(g,-1,sizeof(g)); ess=-1; 30 inc(i,1,n){ll a; int b; scanf("%lld%d",&a,&b); w[i]=a; pe(i,b);} 31 inc(i,1,n)if(!vis[i]){ 32 dfs(i,-1); ll mx=0; 33 memset(f,-1,sizeof(f)); mx=max(mx,dp(es[bad].f,-1,0)); 34 memset(f,-1,sizeof(f)); mx=max(mx,dp(es[bad].t,-1,0)); 35 ans+=mx; 36 } 37 printf("%lld",ans); 38 }
20160519