[NOIP2018 problem solution to improve the group D2T3 defend the kingdom]

Although ddp miss, but the amount of code a bit large ( in fact, I do not ), so this optimization by multiplying the tree DP to solve this problem.

------------
meaning of problems analysis

To stain a tree, each node requires a certain dyeing cost, requires at least two adjacent nodes are dyed to have a given number of restrictions, each restriction request satisfies the condition of minimum cost to much.

Ideas analysis

Solutions without first determined. Obviously, only if $ a, b $ each other father-son relationship (here refers to the parent-child relationship is strictly adjacent), and $ x, y $ are zero when no solution, other cases can be the solution through a multi-staining.

It is easy to think of tree-DP, how to set it so specific state? For each of the two restrictions given points $ a, b $, the answer may be composed of four parts: $ A $ subtree rooted to $ b $ subtree rooted to $ lca (a , B) $ subtree rooted at $ a $ subtracted to the subtree rooted at $ b $ and rooted subtree minus the whole tree subtree $ lca (a, b) $ rooted . Because $ a, $ B $ limit affects from $ a $ b $ to stain state of the chain, thus split into four parts, four parts without affecting each other.

Obviously, $ a, b $ limit will only affect to $ lca (a, b) $ subtree and to $ a $ is the root of the subtree rooted at minus $ b $ is the root of the subtree the answer, therefore, we can answer the other three parts of the pre-out, and then deal with the rest of this part of the DP.

Thus state can be set to: $ f1 [i] [1/0] $ $ I $ expressed in the subtree rooted at $ I $ when dyed / dyed when no minimum cost, $ f2 [i] [1/0 ] represents the entire tree subtracted to $ $ I $ subtree rooted at $ I $ when dyed / dyed when no minimum cost, $ dp [i] [1/0] [1/0] $ $ LCA represented (a, b) $ $ I $ subtracted to the subtree rooted subtree rooted at $ I $ when staining / non-staining and $ I $ parent staining / non-staining (in the order corresponding to the second dimension and when the three-dimensional minimum cost). In this way, we can from $ a, b $ DP computing has been the answer to the $ lca (a, b) $ at the. In particular, no solution when the current state value is set to INF.

But this time complexity is clearly too much. Like this many times in the direction of the tree DP, it is contemplated to optimize the use of tree multiplier. Change my $ dp $ array definition, $ dp [i] [j] [0/1] [0/1] $ expressed in $ i $ the $ 2 ^ j $ generation ancestor is the root of the subtree to minus $ i when the $ i $ $ staining / non-staining and $ i $ subtree rooted in the $ 2 ^ j $ staining generation ancestor / minimum cost when non-staining.

The next question is how the DP. Using three arrays above, we can multiply the DP to $ lca (a, b) $ at. First, we first $ a, b $ DP greater depth a doubling up until $ a, b $ at the same depth; time if $ a = b $, indicating that they have a relationship of the original ancestor and descendant directly the answer output; otherwise it will $ a, b $ DP while doubling up, until the child node $ lca (a, b) $, and then subjected to final processing, output answer. DP when the state transition to enumerate.

Sort out ideas:

1. Pretreatment out $ f1, f2, dp $ three array
1. $ a, b $ upwardly a greater depth in the multiplying until $ a, b $ at the same depth
1. Analyzing this case $ a, b $ are equal, and if so, the answer to the direct output; otherwise it went up
1. $ a, b $ doubled to $ lca (a, b) $ child nodes, for final processing, output answer

The next step will be to explain the specific implementation.

Implementation

1. Pretreatment $ $ array F1

Very foundation of a tree-DP, similar to no party boss, will not repeat them. Also the depth of processing and $ D $ array multiplier array ancestor $ f $.

void dfs1(int fa,int x)
{
    f[x][0]=fa,d[x]=d[fa]+1,f1[x][1]=p[x];
    for(int i=head[x],y;i;i=Next[i])
        if(fa!=(y=ver[i]))
        {
            dfs1(x,y);
            f1[x][0]+=f1[y][1];
            f1[x][1]+=min(f1[y][0],f1[y][1]);
        }
}

2. pretreatment $ f2 $ array

When the $ i $ non-staining, $ i $ father nodes must be dyed, so the answer is when the $ i $ father dyeing, the whole tree subtracted to $ i $ father is the root of the subtree answers, plus to $ i $ brother (ie, the same father node) subtree rooted at the root of the answer. Recalling the process of recursive $ f1 $ array, little brother to $ i $ subtree rooted at the root of the answer when the statistical equivalent of $ i $ statistics, namely $ f1 [fa] [1] -min (f1 [i] [0], f1 [i] [1]) $, where $ I $ $ FA $ represents father.

When the $ i $ staining, $ i $ father dye does not stain can be, when $ i $ father dyeing and answers when the $ i $ not stain is the same, does not stain when empathy statistics, statistical equivalent of $ f1 revoked the statistics when the $ i $ $.

void dfs2(int x)
{
    for(int i=head[x],y;i;i=Next[i])
        if(f[x][0]!=(y=ver[i]))
        {
            f2[y][0]=f2[x][1]+f1[x][1]-min(f1[y][0],f1[y][1]);
            f2[y][1]=min(f2[y][0],f2[x][0]+f1[x][0]-f1[y][1]);
            dfs2(y);
        }
}

3. DP $ $ array pretreatment

First $ dp $ array is initialized to infinity. First, the first processing dp initial value, that is, $ j = 0 $ in.

Obviously, when not dyed $ i $ and $ i $ parent, is no solution, do not touch it; when the $ i $ father dyeing, $ i $ dye does not stain can be, and therefore the answer above $ similar f2 [i] [0] $ transfer, but do not add the rest; $ i $ when the father does not stain, $ i $ must be stained, transfer similar, will not repeat them.

Multiplication is then treated, for the first 2 ^ $ J $ generation ancestor, and the ancestor generation enumeration ^ $ $ $ I $ J and ^ state generation ancestor of $ 2 {j-1} $ 2 for the second transfer. Transfer minds are essentially similar.

void pre () 
{ 
    Memset (DP, 0x3f3f , the sizeof (DP)); 
    DFS1 ( 0 , . 1 ), DFS2 ( . 1 ); // first processing f1, f2 array 
    for ( int I = . 1 ; I <= n-; I ++ ) 
    { 
        DP [I] [ 0 ] [ 0 ] [ . 1 ] DP = [I] [ 0 ] [ . 1 ] [ . 1 ] = F1 [F [I] [ 0 ]] [ . 1 ] -min (F1 [I] [ 0 ], F1 [I] [ . 1 ]); // parent node stained 
        DP [I] [ 0] [ . 1 ] [ 0 ] = [F [I F1] [ 0 ]] [ 0 ] -f1 [I] [ . 1 ]; // parent node does not stain 
    }
     for ( int J = . 1 ; J <= . 19 ; J ++ )
         for ( int I = . 1 ; I <= n-; I ++ ) 
        { 
            int FA = F [I] [J- . 1 ]; 
            F [I] [J] = F [FA] [J- . 1 ]; // first calculation ancestor 
            for ( int X = 0 ; X < 2 ; X ++) //Enumerate the current point status 
                for ( int Y = 0 ; Y < 2 ; Y ++) // Enumeration of 2 ^ j generation ancestor state 
                    for ( int Z = 0 ; Z < 2 ; Z ++) // Enumeration 2 ^ (j-1) state generation ancestor 
                        DP [I] [J] [X] [Y] = min (DP [I] [J] [X] [Y], DP [I] [J- . 1 ] [ X] [Z] + DP [FA] [J- . 1 ] [Z] [Y]); 
        } 
}

Next, the inquiry process to explain.

4. A $, b $ on a larger depth shift until A $, b $ at the same depth

We can shilling a greater depth for the $ a $, if not, then the exchange.

Defines an array of four $ ansa [0/1], ansb [0/1], nowa [0/1], nowb [0/1] $, representing $ a, b $ final answer when non-staining / stain and current answer. Specifically, the answer is the answer to the current multiplier of this ancestor is the root of the subtree.

Prior to processing, because of restrictions, the A $, b $ another state point is initialized to INF.

When transferred, in the same manner, the enumeration state of the intermediate points transferred. Before each transfer, Nowa $ $ array is initialized to INF; After each transfer, the value assigned to the array $ $ $ Nowa ANSA $ array, then move the $ $ a.

上移之后,若$a=b$,则直接返回答案。因为当前点是$b$,而限制条件对$b$起作用,因此答案就是$ansa[y]+f2[a][y]$,即以$b$为根的子树的答案加上剩下部分的答案。

    if(d[a]<d[b])
        swap(a,b),swap(x,y);//令x为深度较大的点
    ansa[1-x]=ansb[1-y]=INF;
    ansa[x]=f1[a][x],ansb[y]=f1[b][y];//初值
    for(int j=19;j>=0;j--)//倍增
        if(d[f[a][j]]>=d[b])//上移到同一深度
        {
            nowa[0]=nowa[1]=INF;//初始化
            for(int u=0;u<2;u++)//枚举2^j辈祖先的状态
                for(int v=0;v<2;v++)//枚举当前点的状态
                    nowa[u]=min(nowa[u],ansa[v]+dp[a][j][v][u]);
            ansa[0]=nowa[0],ansa[1]=nowa[1],a=f[a][j];//数组值转移
        }
    if(a==b)
        return ansa[y]+f2[a][y];//相等直接返回

5. 将$a,b$同时上移到$lca(a,b)$的子节点处

与上一步类似,只是同时上移,就不赘述了。

    for(int j=19;j>=0;j--)
        if(f[a][j]!=f[b][j])
        {
            nowa[0]=nowa[1]=nowb[0]=nowb[1]=INF;
            for(int u=0;u<2;u++)
                for(int v=0;v<2;v++)
                {
                    nowa[u]=min(nowa[u],ansa[v]+dp[a][j][v][u]);
                    nowb[u]=min(nowb[u],ansb[v]+dp[b][j][v][u]);
                }
            ansa[0]=nowa[0],ansa[1]=nowa[1],ansb[0]=nowb[0],ansb[1]=nowb[1],a=f[a][j],b=f[b][j];
        }

6. 对$a,b,lca(a,b)$的状态进行枚举,进行最后的处理

还是一样的思想,若$lca(a,b)$染色,则$a,b$染不染色都行;否则$a,b$必须染色。式子看起来很长,实际上只是之前的式子多了一个节点罢了。

int fa=f[a][0];
    ans0=f1[fa][0]-f1[a][1]-f1[b][1]+f2[fa][0]+ansa[1]+ansb[1];//lca(a,b)不染色
    ans1=f1[fa][1]-min(f1[a][0],f1[a][1])-min(f1[b][0],f1[b][1])+f2[fa][1]+min(ansa[0],ansa[1])+min(ansb[0],ansb[1]);//lca(a,b)染色
    return min(ans0,ans1);

完结撒花~

(所以对于正解来说type并没有什么用,不过在考场上打不出正解的时候确实是拿部分分的一个好助手)

------------
最后奉上完整代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=2e5,M=3e5,L=20;
const ll INF=1e17;
int n,m,tot;
int p[N],d[N],f[N][L];
int head[N],ver[2*M],Next[2*M];
ll ans0,ans1;
ll nowa[2],nowb[2],ansa[2],ansb[2],f1[N][2],f2[N][2],dp[N][L][2][2];
string tp;
void add(int x,int y)
{
    ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
    ver[++tot]=x,Next[tot]=head[y],head[y]=tot;
}
void dfs1(int fa,int x)
{
    f[x][0]=fa,d[x]=d[fa]+1,f1[x][1]=p[x];
    for(int i=head[x],y;i;i=Next[i])
        if(fa!=(y=ver[i]))
        {
            dfs1(x,y);
            f1[x][0]+=f1[y][1];
            f1[x][1]+=min(f1[y][0],f1[y][1]);
        }
}//1. 预处理 $f1$ 数组
void dfs2(int x)
{
    for(int i=head[x],y;i;i=Next[i])
        if(f[x][0]!=(y=ver[i]))
        {
            f2[y][0]=f2[x][1]+f1[x][1]-min(f1[y][0],f1[y][1]);
            f2[y][1]=min(f2[y][0],f2[x][0]+f1[x][0]-f1[y][1]);
            dfs2(y);
        }
}//2. 预处理 $f2$ 数组
void pre()
{
    memset(dp,0x3f3f,sizeof(dp));
    dfs1(0,1),dfs2(1);
    for(int i=1;i<=n;i++)
    {
        dp[i][0][0][1]=dp[i][0][1][1]=f1[f[i][0]][1]-min(f1[i][0],f1[i][1]);
        dp[i][0][1][0]=f1[f[i][0]][0]-f1[i][1];
    }
    for(int j=1;j<=19;j++)
        for(int i=1;i<=n;i++)
        {
            int fa=f[i][j-1];
            f[i][j]=f[fa][j-1];
            for(int x=0;x<2;x++)
                for(int y=0;y<2;y++)
                    for(int z=0;z<2;z++)
                        dp[i][j][x][y]=min(dp[i][j][x][y],dp[i][j-1][x][z]+dp[fa][j-1][z][y]);
        }
}//3. 预处理 $dp$ 数组
bool check(int a,int x,int b,int y)
{
    return !x && !y &&(f[a][0]==b || f[b][0]==a);
}
ll ask(int a,int x,int b,int y)
{
    if(d[a]<d[b])
        swap(a,b),swap(x,y);
    ansa[1-x]=ansb[1-y]=INF;
    ansa[x]=f1[a][x],ansb[y]=f1[b][y];
    for(int j=19;j>=0;j--)
        if(d[f[a][j]]>=d[b])
        {
            nowa[0]=nowa[1]=INF;
            for(int u=0;u<2;u++)
                for(int v=0;v<2;v++)
                    nowa[u]=min(nowa[u],ansa[v]+dp[a][j][v][u]);
            ansa[0]=nowa[0],ansa[1]=nowa[1],a=f[a][j];
        }
    if(a==b)
        return ansa[y]+f2[a][y];//4. 将 $a,b$ 中深度较大的一个上移,直到 $a,b$ 处于同一深度
    for(int j=19;j>=0;j--)
        if(f[a][j]!=f[b][j])
        {
            nowa[0]=nowa[1]=nowb[0]=nowb[1]=INF;
            for(int u=0;u<2;u++)
                for(int v=0;v<2;v++)
                {
                    nowa[u]=min(nowa[u],ansa[v]+dp[a][j][v][u]);
                    nowb[u]=min(nowb[u],ansb[v]+dp[b][j][v][u]);
                }
            ansa[0]=nowa[0],ansa[1]=nowa[1],ansb[0]=nowb[0],ansb[1]=nowb[1],a=f[a][j],b=f[b][j];
        }//5. 将 $a,b$ 同时上移到 $lca(a,b)$ 的子节点处
    int fa=f[a][0];
    ans0=f1[fa][0]-f1[a][1]-f1[b][1]+f2[fa][0]+ansa[1]+ansb[1];
    ans1=f1[fa][1]-min(f1[a][0],f1[a][1])-min(f1[b][0],f1[b][1])+f2[fa][1]+min(ansa[0],ansa[1])+min(ansb[0],ansb[1]);
    return min(ans0,ans1);//6. 对 $a,b,lca(a,b)$ 的状态进行枚举,进行最后的处理
}
int main()
{
    scanf("%d%d",&n,&m);cin>>tp;
    for(int i=1;i<=n;i++)
        scanf("%d",&p[i]);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
    }
    pre();
    for(int i=1,a,x,b,y;i<=m;i++)
    {
        scanf("%d%d%d%d",&a,&x,&b,&y);
        if(check(a,x,b,y))
        {
            puts("-1");
            continue;
        }
        printf("%lld\n",ask(a,x,b,y));
    }
    return 0;
}

Guess you like

Origin www.cnblogs.com/TEoS/p/11785221.html
Recommended