CF #586 Div.2+1 E. Tourism //边双+树形dp

题目

题意

给一个无向图(无自环,无重边),每个点有一个分数 w i w_i wi l y ly ly s s s点出发,每次不能回到前驱,求ly能得到的最大积分。

思路

先边双缩点。
然后以 s s s为根,得到一个 d f s dfs dfs树,如果缩点前的点数大于1,那么这个点是可以经过后反向的。
然后考虑树形 d p dp dp,贪心遍历尽量多的可以反向的点,再考虑从哪个儿子下去并且不反向回来。

/*   Author : Rshs
 *   Data : 2019-10-03-09.46
 */
#include
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MXN = 1e6+5;
struct no{
    int to,num;
};
vector<no>g[MXN];
vector<int>G[MXN];
int sa[MXN],sb[MXN];
int col[MXN]; //Ⱦɫ
int dfn[MXN],isbridge[MXN],dfs_clock,ebc_cnt;
/*�������   �ŷ�[0,2*m-1]  ʱ���   ��˫����*/
vector<int>v[MXN];int canback[MXN];
LL bk[MXN],W[MXN],w[MXN],cc[MXN];
int tarjan(int u,int fa){
    int lowu=dfn[u]=++dfs_clock;
    for(auto ii:g[u]){
        int v=ii.to,num=ii.num;
        if(!dfn[v]){
            int lowv=tarjan(v,u);
            lowu=min(lowu,lowv);
            if(lowv>dfn[u]){ //u->v�Ǹ��
                isbridge[num]=isbridge[num^1]=1;
            }
        }
        else if(v!=fa&&dfn[v]<dfn[u]){
            lowu=min(lowu,dfn[v]);  //������u������(�����
        }
    }
    return lowu;
}

void colourDfs(int now,int colNum){
    col[now]=colNum;
    for(auto i:G[now]) {
        if(!col[i]) colourDfs(i,colNum);
    }
}

void ebcGet(int n,int m){
    for(int i=1;i<=n;i++) dfn[i]=col[i]=0;
    for(int i=0;i<=2*m;i++) isbridge[i]=0;
    for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i,-1);
    for(int i=1;i<=m;i++){
        if(!isbridge[i*2-1]) {
            G[sa[i]].push_back(sb[i]);
            G[sb[i]].push_back(sa[i]);
        }
    }
    int colNum=0;
    for(int i=1;i<=n;i++) if(!col[i]) colourDfs(i,++colNum);
    for(int i=1;i<=n;i++) v[col[i]].push_back(i);
    for(int i=1;i<=n;i++) G[i].clear();
    for(int i=1;i<=m;i++) {
        if(col[sa[i]]!=col[sb[i]])
            G[col[sa[i]]].push_back(col[sb[i]]),G[col[sb[i]]].push_back(col[sa[i]]);
    }
    for(int i=1;i<=n;i++)W[col[i]]+=w[i];
    for(int i=1;i<=colNum;i++) canback[i]=(SZ(v[i])>1);
}

void dfs(int v,int pr){  //统计回到v时反向可以得到的最大价值
    LL tt=0;
    for(auto i:G[v]){
        if(i==pr)continue;
        dfs(i,v);
        tt+=bk[i];
        if(canback[i])canback[v]=1; //只要儿子可以反向,那么自己也可以反向
    }
    if(canback[v])bk[v]=tt+W[v];

}

void redfs(int v,int pr){ //统计以v为根的子树的最大价值
    if(canback[v]){ //可以反向
        cc[v]=bk[v];
        for(auto i:G[v]){
            if(i==pr)continue;
            redfs(i,v);
            cc[v]=max(cc[v],bk[v]-bk[i]+cc[i]);
        }
        return ;
    }
    //不能反向
    cc[v]=W[v];
    for(auto i:G[v]){
        if(i==pr)continue;
        redfs(i,v);
        cc[v]=max(cc[v],W[v]+cc[i]);
    }

}
int main(){
    int n,m;cin>>n>>m;

    for(int i=1;i<=n;i++){
        int sa;scanf("%d",&sa);w[i]=sa;
    }
    for(int i=1;i<=m;i++){
        scanf("%d %d",&sa[i],&sb[i]);
        g[sa[i]].push_back(no{sb[i],i*2-2});
        g[sb[i]].push_back(no{sa[i],i*2-1});
    }
    ebcGet(n,m);
    int sr;cin>>sr;
    dfs(col[sr],-1);
    redfs(col[sr],-1);
    cout<<cc[col[sr]];
    return 0;
}

你可能感兴趣的:(CF #586 Div.2+1 E. Tourism //边双+树形dp)