zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    安装一个Linux 罗晓杜
    基本正则表达 罗晓杜
    安装MySQL 罗晓杜
    复选框返回选中项ID到后台
    软件工程的理解
    作业一:计科131邵楠
    javascript中函数和方法的区别
    Javascript动画系列之 —— lightbox实现(一)
    如何让自己的javascript代码具有可维护性?
    javascript动画系列 —— 切换图片(原生)
  • 原文地址:https://www.cnblogs.com/xryjr233/p/BZOJ1040.html
Copyright © 2011-2022 走看看