[2007] IOIトレーニングパス

ツリー状の圧力DP + DP

トピックリンク

まず第一に、あなたはエッジがツリー(ナンセンス)を形成し、削除することはできません

だから我々はそれを得るために木を置きます。

なぜなら

彼らの訓練の同じ強度を確保するために、彼らは道路の偶数でパスを選択する必要があります。

そう非ツリーエッジエンドポイントの残りの部分は二重リングを構成し、この非ツリーエッジが削除されることが確実です。

そして理由

私は、市内各地途中でしたことがない、と(同じ方向かどうかにかかわらず)同じ道に二回乗ることはありません

その後も、非ツリーリング側を形成した後に削除されます、我々は2つの奇数のツリーリング側相対的な重みがある場合は、明らかにそれが正当でないことがわかります。

したがって、ツリーの各側は、それは一度だけ横断されます。

そこで、我々は、逆さまに得ることができます。ツリーの各側は、それが一度だけ、など横断するような非ツリーエッジの選択いくつかの部分、可能な限りこれらのエッジの重みとして大きいこと。

タイトルを見てみましょう

また、各都市は101010個のエンドポイントの道路までです

この点で、形状の圧力を検討:\(DP [X] [STA]を\)現在のノードが選択された息子のSTA(1件は、選択された)と最大右側の状態であり、X、であることを示しています。

そして、非ツリーエッジのために、2つの決定があります。

それのための1票。(図を参照)

すなわち、バック我々はLCAのようにすべての非ツリーエッジを、それを除去し、そしてポイントに、次いでプレヴァルカタツムリ式(Nとにかく1000年前)

そして、うまく転送。

2.それに投票しないでください。

これは、アップ比較的容易です。

状態のSTAのために、我々はちょうどその疲れのこれらの息子の値をDPとそれを乗り越える、選択された息子を見つける必要があります。

コード:

#include<bits/stdc++.h>
#define MAXN 10010
using namespace std;
int n,m,tot,head[MAXN],cnt,pre[MAXN][20],deep[MAXN],lg[MAXN],dp[1050][1050],num[1010][1010],Cnt[MAXN],ansSum,ans;
struct sgt {
    int st,ed,v,lca,sum,X,Y;
} E[MAXN];
struct node {
    int ed,last;
} G[MAXN<<1];
vector<int> Q[MAXN];
void Add(int st,int ed) {
    tot++;
    G[tot]=node {ed,head[st]};
    head[st]=tot;
}
void DFS(int x,int fa){
    deep[x]=deep[fa]+1;
    pre[x][0]=fa;
    for(int i=1;(1<<i)<=deep[x];i++)pre[x][i]=pre[pre[x][i-1]][i-1];
    for(int i=head[x];i;i=G[i].last){
        int t=G[i].ed;
        if(t==fa)continue;
        DFS(t,x);
    }
}
int LCA(int x,int y){
    if(deep[x]<deep[y])swap(x,y);
    while(deep[x]>deep[y])x=pre[x][lg[deep[x]-deep[y]]-1];
    if(x==y)return x;
    for(int i=lg[deep[x]]-1;i>=0;i--){
        if(pre[x][i]==pre[y][i])continue;
        x=pre[x][i],y=pre[y][i];
    }
    return pre[x][0];
}
int jump(int x,int y,int id){
    if(x==y)return 0;
    int sonx=x;
    x=pre[x][0];
    while(x!=y){
        E[id].sum+=dp[x][((1<<Cnt[x])-1)^num[x][sonx]];
        sonx=x;
        x=pre[x][0];
    }
    return sonx;
}
void solve(int x,int fa){
    for(int i=head[x];i;i=G[i].last){
        int t=G[i].ed;
        if(t==fa)continue;
        num[x][t]=1<<Cnt[x];
        Cnt[x]++;
        solve(t,x);
    }
    for(int i=0;i<Q[x].size();i++){
        int id=Q[x][i],st=E[id].st,ed=E[id].ed;
        E[id].sum=dp[st][(1<<Cnt[st])-1]+dp[ed][(1<<Cnt[ed])-1]+E[id].v;
        E[id].X=jump(st,x,id);
        E[id].Y=jump(ed,x,id);
    }
    for(int i=0;i<(1<<Cnt[x]);i++){
        int res=0;
        for(int j=head[x];j;j=G[j].last){
            int t=G[j].ed;
            if(t==fa)continue;
            if(num[x][t]&i)res+=dp[t][(1<<Cnt[t])-1];
        }
        dp[x][i]=max(dp[x][i],res);
    }
    for(int i=0;i<(1<<Cnt[x]);i++){
        for(int j=0;j<Q[x].size();j++){
            int id=Q[x][j],X=E[id].X,Y=E[id].Y;
            if((num[x][X]&i)||(num[x][Y]&i))continue;
            dp[x][i|num[x][X]|num[x][Y]]=max(dp[x][i|num[x][X]|num[x][Y]],dp[x][i]+E[id].sum);
        }
    }
}
int main() {
    for(int i=1;i<=MAXN-10;i++)lg[i]=lg[i-1]+((1<<lg[i-1])==i);
    scanf("%d %d",&n,&m);
    for(int x,y,z,i=1; i<=m; i++) {
        scanf("%d %d %d",&x,&y,&z);
        if(z==0) {
            Add(x,y);
            Add(y,x);
        }
        else {
            cnt++;
            E[cnt]=sgt{x,y,z};
        }
    }
    DFS(1,0);
    for(int i=1;i<=cnt;i++){
        E[i].lca=LCA(E[i].st,E[i].ed);
        if((deep[E[i].st]+deep[E[i].ed]-2*deep[E[i].lca])&1)ans+=E[i].v;    
        else Q[E[i].lca].push_back(i),ansSum+=E[i].v;
    }
    solve(1,0);
    ans+=ansSum-dp[1][(1<<Cnt[1])-1];
    cout<<ans;
    return 0;
}

おすすめ

転載: www.cnblogs.com/SillyTieT/p/11486037.html