[NOIP2018問題の解決策は、グループD2T3は王国を守る向上させるために]

DDPミス、少し大きなコードの量が、(実際には、私はそうではない)ので、木DPを乗算することにより、この最適化は、この問題を解決します。

------------
問題分析の意味

ツリーを染色するために、各ノードは、特定の染色コストを必要とする少なくとも2つの隣接するノードが制約の所与の数を有するように染色される必要があり、各規制要求がはるかに最小コストの条件を満足します。

アイデア解析

最初に決定せずにソリューション。もちろん、唯一の$、$ B同士父と息子の関係ならば(ここでは、親子関係を参照することは厳密に隣接している)、および$ X、Yの$解決策、その他の場合は、マルチ染色によるソリューションになることはできませんとき0です。

それをとても具体的な状態を設定する方法を、ツリー-DPを考えるのは簡単ですか!$ LCA(Aに根ざし$ Bが$サブツリーに根ざし$ $サブツリー:指定された2つの制約点のそれぞれは、B $を$ため、答えは、4つの部分から構成されてもよいです$ B $とルートとするサブツリーを引いツリー全体のサブツリーの$ LCA(a、b)は$根ざしをルートとするサブツリーに減算$ $をルートと、B)$サブツリー。$ので、$ Bは$制限はお互いに影響を与えることなく、四つの部分、4つの部分に分割し、したがって、チェーンの状態を染色するために$ $ Bが$からの影響します。

明らかに、A、Bの$制限はたったの$のLCA(a、b)は$サブツリーに影響を与えますし、$に$がマイナス$ bは$をルートとするサブツリーのルートである$サブツリーのルートであります答えは、それゆえ、我々は前出の他の三つの部分に答えることができ、その後、DPのこの部分の残りの部分を扱っています。

$ f1の[i]を[1/0] $ $ I $ときなし最小コスト、$ f2を染め/染めたときに$ I $をルートとするサブツリーで表現[I] [1/0:このような状態は、に設定することができます】最低コストは、$ DP [I] [1/0] [1/0] $ $ LCAが示さない場合染め/染めたとき$ I $をルート$ $ I $サブツリーに減算ツリー全体を表します$ I $染色/非染色および$ I $親染色/非染色(ルートとするサブツリールートとするサブツリーに減算(a、b)は$ $ I $第二の次元に対応するために、及びとき3次元最小コスト)。このように、我々は$ DPコンピューティングB $から$のLCAで(a、b)は$に答えてきたことができます。特に、現在の状態値をINFに設定されない溶液。

しかし、この時の複雑さは明らかに多すぎます。ツリーDPの方向にこの何回も同じように、ツリー乗算器の使用を最適化することが考えられます。私の$のdp $配列の定義は、私は$ 2 ^ j個の$生成祖先を$ [i] [j]は[0/1] [0/1] $ $をで表現$ dpと、変更することはマイナスの$ iをサブツリーのルートでありますときの$ I $の$染色/非染色および$ 2 ^ J $染色生成祖先/最小コスト際に非染色に根ざし$ I $サブツリー。

次の質問はどのようにDPです。上記の3つの配列を使用して、我々は、$ LCAで(a、b)は$にDPを掛けることができます。まず、最初に$、$ DP Bより深く、同じ深さで$、Bの$まで倍増、時間の$ A = Bの$であれば、彼らは直接、元の祖先と子孫の関係を持っていることを示します答え出力は、最大倍加しながらそれ以外の場合は、子ノードの$ LCA(a、b)は$まで、$ DP B、$し、その後最終処理、出力回答を行いました。DPの状態遷移を列挙する。

アイデアを整理:

1.前処理アウト$のF1、F2、$ 3つのアレイDP
1 $、$、B $同じ深さまで乗算で上方$大きい深さB
1解析この場合の$ A、Bの$等しく、そうであれば、直接出力に答え、そうでなければそれは上がっ
1 $、$は、$ LCA(a、b)は$子ノード、最終処理のために、出力応答に倍増bは

次のステップは、特定の実装を説明することになります。

実現

1.前処理$ $アレイF1

ツリー-DPの根幹、無パーティーボスと同様に、それらを繰り返すことはしません。また、処理の深さと$ D $アレイ乗算器アレイ祖先$ fが$。

ボイド DFS1(int型、FAをINT X)
{ 
    [X] [F 0 ] = FA、D [x]は、D [FA] + = 1、[X] F1 [ 1 ] = P [X]を。
    以下のためにint型 ; I I = I =ヘッド[X]、yは次に[I])
         であれば(FAを=(Y =!版[I]))
        { 
            DFS1(X、Y)
            F1 [X] [ 0 ] + = F1 [Y] [ 1 ]。
            F1 [X] [ 1 ] + =分(F1 [Y] [ 0 ]、F1 [Y] [ 1 ])。
        } 
}

2.前処理$ F2は$配列

$ iが非染色を$ときは、$ iが$父ノードが染めしなければならないので、$ I $父の染色は、木全体が$に減じたときに答えがある私$父は、サブツリーの回答のルートであるプラス答えのルートに根ざし$ I $弟(すなわち、同じ父ノード)サブツリーへ。$に再帰$ F1は$配列、弟のプロセスを想起I $の統計的同等iは、すなわち、$ F1 [FA] [1] -minを統計値を$答えのルート(f1で根付い$サブツリー$ I $ $ FA $は父を表し[I] [0]、F1 [i]は、[1])$、。

$ Iの$染色は、$ iの$父親色素がシミができていない場合は、私が汚しません$ $が同じであれば、とき$ I $父染色と回答、染色しないとき共感統計、統計的同等$のF1統計を取り消されたとき、$ Iの$の$。

ボイド DFS2(INT X)
{ 
    ためint型 I =ヘッド[X]、Y、Iは、I = 次に[I])
         であれば(F [x]は[ 0!] =(Y = 版[I]))
        { 
            F2 [Y] [ 0 ] = F2 [X] [ 1 ] + F1 [X] [ 1 ] -min(F1 [Y] [ 0 ]、F1 [Y] [ 1 ])。
            F2 [Y] [ 1 ] =分(F2 [Y] [ 0 ]、F2 [X] [ 0 ] + F1 [X] [ 0 ] -f1 [Y] [ 1 ])。
            DFS2(Y)。
        } 
}

3. DP $ $アレイの前処理

最初の$のdp $アレイは無限大に初期化されます。まず、第一の処理DP初期値、つまり、の$ J = 0 $。

$ I $父の染色は、$ iの色素がシミができていない$とき、および$上記のため、答えは、明らかに、ときに私は何の解決策はありません$と$ I $の親、$染めていない、それを触れないでください同様のF2 [I] [0] $転送が、残りの部分を追加しないでください。$ I $父親は染色しないとき、$ iの$は、同様の転送、染色されている必要があり、それらを繰り返すことはしません。

乗算は、第1の2 ^ $ Jの$生成祖先のために、処理され、二次転写のための先祖生成列挙^ $ $ $ I $ J及び{J-1} $ 2 $ 2の^状態生成祖先。転送の心は、本質的に似ています。

ボイドプレ()
{ 
    memsetの(DP、0x3f3fはsizeof (DP)); 
    DFS1(01)、DFS2(1); // 最初の処理F1、F2アレイ
    のための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 ] = [F [I F1] [ 0 ] [ 0 ] -f1 [I] [ 1 ]; // 親ノードは染色しない
    }
     のためにINT J = 1。 ; J <= 19。 ; J ++ のためのINT I = 1 ; I <= N; I ++ 
        { 
            int型 FA = F [I]、[J- 1 ]、
            F [I] [J] = F [FA] [J- 1 ]; // 最初の計算祖先
            のためにINT X = 0 ; X < 2 ; X ++)//現在のポイント状況列挙
                INT Y = 0 ; Y < 2、Y ++)// 2 ^ jを生成祖先状態の列挙
                    のためにINT Z = 0 ; Z < 2 ; Z ++)// 列挙2 ^(J-1)状態生成祖先 
                        DP [I] [J] [X] [Y] =分(DP [I] [J] [X] [Y]、DP [I]、[J- 1 ] [ X] [Z] + DP [FA] [J- 1 ] [Z] [Y]); 
        } 
}

次に、問い合わせ処理を説明します。

4. $、$まで深くシフト上のB $、同じ深さのB $

いない場合我々は、その後、交換を$ $についてより深くシリングことができます。

定義$最終的な答えB $表す4 $アンサ[0/1]のansB [0/1]、ノワ[0/1]、nowb [0/1] $、の配列、場合非染色/染色及び現在の答え。具体的には、その答えは、この祖先の現在の乗数に対する答えはサブツリーのルートです。

なぜなら制約の、処理に先立ち、A $は、$ bを別の状態点はINFに初期化されます。

同様に、転送するとき、中間点の列挙状態が転送されます。各転送の前に、ノワ$ $アレイはINFに初期化され、各転送後、値は配列$ $ $ノワANSA $アレイに割り当てられ、その後、$ $移動します。

上移之后,若$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;
}

おすすめ

転載: www.cnblogs.com/TEoS/p/11785221.html