[2019.3.5]BZOJ1040 [ZJOI2008]骑士

我们把每一个骑士看做一个点,和他厌恶的骑士之间连一条双向边。

那么问题变为求基环树森林上的最大权独立集。

我们先考虑一颗基环树上的情况。

下图是一棵基环树,蓝边为环上的边。

图炸了QWQ

发现每一块连在一起的非环边(红边)构成一棵树,而且明显每一棵树中有且仅有一个环上的点。

那么我们把每棵树中环上的点作为根,设\(f_{i,0/1}\)表示\(i\)及其子树中,点\(i\)选/没选的最大权独立集的权值。

\(f\)的求得就是一个普通的树形动规问题。

于是我们考虑环上的点。

\(f_{i,1}\)看作选择点\(i\)的权值,\(f_{i,0}\)看作不选点\(i\)的权值。

于是我们要求环上的最大权独立集。

一个简单的环形动规问题。

任选环上一点\(s\)开始dp。

\(dp_{i,0/1,0/1}\)表示考虑\(s\)\(i\)的路径上,\(s\)选/不选,点\(i\)选/不选,得到的最大权值。

那么

\(dp_{s,0,0}=f_{s,0}\)

\(dp_{s,1,1}=f_{s,1}\)

\(dp_{s,0,1}=dp_{s,1,0}=-\infty\)

\(i\)的上一个点为\(j\),那么

\(dp_{i,0,0}=max(dp_{j,0,1},dp_{j,0,0})+f_{i,0}\)

\(dp_{i,1,0}=max(dp_{j,1,1},dp_{j,1,0})+f_{i,0}\)

\(dp_{i,0,1}=dp_{j,0,0}+f_{i,1}\)

\(dp_{i,1,1}=dp_{j,1,0}+f_{i,1}\)

设环的终点为\(t\),则答案=\(max(dp_{t,1,0},dp_{t,0,1},dp_{t,0,0})\)

所有基环树的答案总和就是基环树森林的答案。

code:

#include<bits/stdc++.h>
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const long long FINF=-1e16;
struct edge{
    int t,nxt;
}e[2000010];
int n,w[1000010],u,cnt,be[1000010],d[1000010],tg[1000010],vis[1000010];
long long f[1000010][2],dp[1000010][2][2],ans;
char buf[1<<21],*p1=buf,*p2=buf;
queue<int>q;
void add(int x,int y){
    e[++cnt].t=y,e[cnt].nxt=be[x],be[x]=cnt,++d[y];
}
void bfs(){
    for(int i=1;i<=n;++i)d[i]==1?q.push(i),tg[i]=1:0;
    while(!q.empty()){
        u=q.front(),q.pop();
        for(int i=be[u];i;i=e[i].nxt)--d[e[i].t]==1?q.push(e[i].t),tg[e[i].t]=1:0;
    }
}
void Merge(int x,int y){
    f[x][0]+=max(f[y][0],f[y][1]);
    f[x][1]+=f[y][0];
}
void dfs(int x){
    f[x][1]=w[x];
    for(int i=be[x];i;i=e[i].nxt)!f[e[i].t][1]&&tg[e[i].t]?dfs(e[i].t),Merge(x,e[i].t),0:0;
}
void Upd(int x,int y){
    dp[x][1][0]=max(dp[y][1][1],dp[y][1][0])+f[x][0];
    dp[x][0][0]=max(dp[y][0][1],dp[y][0][0])+f[x][0];
    dp[x][1][1]=dp[y][1][0]+f[x][1];
    dp[x][0][1]=dp[y][0][0]+f[x][1];
}
void DP(int x){
    int tag=1;
    vis[x]=1;
    for(int i=be[x];i&&tag;i=e[i].nxt)!tg[e[i].t]&&!vis[e[i].t]?Upd(e[i].t,x),DP(e[i].t),tag=0:0;
    ans+=tag*max(dp[x][1][0],max(dp[x][0][1],dp[x][0][0]));
}
void scan(int &x){
    x=0;
    char c=getchar();
    while(!isdigit(c))c=getchar();
    while(isdigit(c))x=x*10+c-'0',c=getchar();
}
int main(){
    scan(n);
    for(int i=1;i<=n;++i)scan(w[i]),scan(u),add(i,u),add(u,i);
    bfs();
    for(int i=1;i<=n;++i)!tg[i]?dfs(i),0:0;
    for(int i=1;i<=n;++i)!tg[i]&&!vis[i]?dp[i][1][1]=f[i][1],dp[i][0][0]=f[i][0],dp[i][1][0]=dp[i][0][1]=FINF,DP(i),0:0;
    printf("%lld",ans);
    return 0;
}
posted @ 2019-03-17 18:16  xryjr233  阅读(160)  评论(0编辑  收藏  举报