noip2018D2T3保卫王国题解

题目:click here.

题意

大概是说给你一棵树,每个点给一个权值\(a_i\),对于任意一条边,两边的点至少选择一个的最小代价。

还有\(q\)个询问,每个询问限制两个点选或不选,再输出最小代价。

~话说这题考场上一看,一脸懵逼,最后硬干一个44分的暴力,然后才发现可以多弄个8分。

动态\(dp\)就算了,考场上谁会没事闲着放着前两题不做去敲动态\(dp\).

尝试暴力

话说这44分的摆着就是皇宫看守。

\(F[i][0/1]\)表示以\(i\)为根的子树,\(i\)选或不选所产生的最小代价。

状态转移就很轻松的出来了:

\[F[x][0]=\sum_{y\in son(x)}F[y][1], F[x][1]=\sum_{y\in son(x)}min(F[y][0],F[y][1])\]

44分就拿到了。

然后再看看那个\(B\),发现每次询问都只会改变从\(a\)\(b\)路上的权值,所以每次修改链上的\(dp\)值,就又拿到了8分。

尝试优化

这样,我们搬回刚才那个转移方程。

\[F[x][0]=\sum_{y\in son(x)}F[y][1], F[x][1]=\sum_{y\in son(x)}min(F[y][0],F[y][1])\]

于是,就可以发现,这个\(F\)数组的\(dp\)值是满足可加性的,于是就有了倍增。

对于每一条边,它的转移是这样的\(F[x][0]+=F[y][1], F[x][1]+=min(F[y][0],F[y][1])\).

那么我们可以让\(F[x][0/1]\)去代表以\(x\)为根的子树的\(dp\)值。

倍增优化

为了方便,我们放几张图表示转移过程。

这里用几种颜色分别代表不同区域的\(dp\)值。

显然\(F[x][0/1]\)代表的是\(k5\)部分的\(dp\)值。

\(G[x][0/1]\)表示以\(x\)为根的子树,除\(x\)子树外的整棵树的\(dp\)值(也就是图中的\(k1-k5\)部分),从图中,我们可以得知\(k1-k5=(k1-k4)+(k4-k5).\)

于是这很容易得到这样一个状态转移:

\[G[x][0]=G[fa][1]+F[fa][1]-min(F[x][0],F[x][1]).\]

通过上面\(F\)的转移方程可以知道\(F[fa][1]-min(F[x][0],F[x][1])\)就是\(k4-k5\)部分。同样的,

\[G[x][1]=min\{G[fa][1]+F[fa][1]-min(F[x][0],F[x][1]),G[fa][0]+F[fa][0]-F[x][1]\}.\]

因为要用倍增,设\(anc1\)表示\(x\)\(2^j\)祖先,显然我们就要从\(x\)\(dp\)值转移到\(anc1\).

\(x\)\(dp\)值是\(k5\)部分,\(anc1\)\(dp\)值是\(k2\)部分,想要从\(x\)转移到\(anc1\),我们就要在\(k5\)上加上\(k2-k5\)部分。

\(f[x][j][0/1][0/1]\)表示\(x\)选或不选,\(x\)\(2^j\)祖先即\(anc1\)选或不选所产生的\(k2-k5\)部分的\(dp\)值,显然转移的时候\(F[x][0/1]+f[x][j][0/1][0/1]\)就可以快速转移到\(F[anc1][0/1]\)了,被限制了的就加个\(inf\).

求这个数组同样不难,我们设\(anc2\)表示\(x\)\(2^{j-1}\)祖先,由图同样\(k2-k5=(k2-k3)+(k3-k5).\)

于是我们这样转移:

\[f[x][j][0/1][0/1]=min\{f[x][j-1][0/1][0]+f[anc2][j-1][0][0/1],f[x][j-1][0/1][1]+f[anc2][j-1][1][0/1]\}.\]

关于\(f\)数组的初始化,同样也是把它们拆成各种区间去累加。

f[x][0][0][0] = inf;
f[x][0][1][0] = F[Fa][0] - F[x][1];
f[x][0][0][1] = f[x][0][1][1] = F[Fa][1] - min(F[x][0], F[x][1]);

倍增的时候就按照\(LCA\)的样子,加上稍微复杂一些的转移就行了。

从这幅图中我们可以看出答案

LCA不选:

\[Ans=G[LCA][0]+F[LCA][0]-F[x][1]-F[y][1]+nowx[1] nowy[1];\]

LCA选:

\[Ans=G[LCA][1]+F[LCA][1]-min(F[x][0],F[x][1])-min(F[y][0],F[y][1])+min(nowx[0],nowx[1])+min(nowy[0],nowy[1])\]

两者取\(min\)就行了。

LL getans(int x,int a,int y,int b)
{
    //为了方便(其实是看着更顺眼),把x和a,y和b互换了一下 
    LL ancx[2] = {inf, inf}, ancy[2] = {inf, inf};
    //ancx,ancy表示x或y的祖先选或不选所产生的以其祖先为根的子树的dp值 
    LL nowx[2] = {inf, inf}, nowy[2] = {inf, inf};
    //nowx,nowy表示当前节点(x和y)为根的子树的dp值 
    //以上都是加了限制的dp值 
    if(dep[x] < dep[y]) swap(x, y), swap(a, b);
    nowx[a] = F[x][a]; nowy[b] = F[y][b];
    for(int j = Log[dep[x]];j >= 0;j --)
      if(dep[fa[x][j]] >= dep[y])
      {
        ancx[0] = ancx[1] = inf;
        for(int fx = 0;fx < 2;fx ++)
          for(int fanc = 0;fanc < 2;fanc ++)
            ancx[fanc] = min(ancx[fanc], nowx[fx] + f[x][j][fx][fanc]);
        nowx[0] = ancx[0]; nowx[1] = ancx[1]; x = fa[x][j];
      }
    if(x == y) return nowx[b] + G[y][b];
    for(int j = Log[dep[x]];j >= 0;j --)
      if(fa[x][j] != fa[y][j])
      {
        ancx[0] = ancx[1] = ancy[0] = ancy[1] = inf;
        for(int fx = 0;fx < 2;fx ++)
          for(int fanc = 0;fanc < 2;fanc ++)
          {
            ancx[fanc] = min(ancx[fanc], nowx[fx] + f[x][j][fx][fanc]);
            ancy[fanc] = min(ancy[fanc], nowy[fx] + f[y][j][fx][fanc]);
          }
          nowx[0] = ancx[0]; nowx[1] = ancx[1]; nowy[0] = ancy[0]; nowy[1] = ancy[1];
          x = fa[x][j]; y = fa[y][j];
      }
    int LCA = fa[x][0];//LCA连接也要分情况讨论 
    LL Ans = G[LCA][0] + F[LCA][0] - F[x][1] - F[y][1] + nowx[1] + nowy[1];
    Ans=min(Ans,G[LCA][1]+F[LCA][1]-min(F[x][0],F[x][1])-min(F[y][0],F[y][1])+min(nowx[0],nowx[1])+min(nowy[0],nowy[1]));
    return Ans;
}

完整代码

#include<bits/stdc++.h>
using namespace std;
#define maxn 110000
#define LL long long
#define inf 1e10 + 1
int n,m,val[maxn];
LL F[maxn][2],G[maxn][2],f[maxn][20][2][2];
int fa[maxn][20],last[maxn],p,dep[maxn],Log[maxn] = {- 1};
LL ans;
inline int read()
{
    int res = 0, flag = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-')flag = - 1;c = getchar();}
    while(c >= '0' && c <= '9'){res = (res << 3) + (res << 1) + (c ^ 48);c = getchar();}
    return flag * res;
}
struct edge
{
    int x,y,next;
    void Add_edge(int X, int Y)
    {
        x = X; y = Y; next = last[x]; last[x] = p;
    }
}e[maxn * 2];
void dfs1(int x,int Fa)
{
    F[x][0] = 0; F[x][1] = val[x]; fa[x][0] = Fa;
    for(int k = last[x];k;k = e[k].next)
      if(e[k].y != Fa)
      {
        int y = e[k].y;
        dep[y] = dep[x] + 1; dfs1(y, x);
        F[x][0] += F[y][1];
        F[x][1] += min(F[y][0], F[y][1]);
      }
}
void dfs2(int x,int Fa)
{
    G[x][0] = G[Fa][1] + F[Fa][1] - min(F[x][0], F[x][1]);
    G[x][1] = min(G[x][0], G[Fa][0] + F[Fa][0] - F[x][1]);
    if(x == 1) G[x][0] = G[x][1] = 0;
    for(int k = last[x];k;k = e[k].next)
      if(e[k].y != Fa)
      {
        int y = e[k].y;
        dfs2(y, x);     
      }
    f[x][0][0][0] = inf;
    f[x][0][1][0] = F[Fa][0] - F[x][1];
    f[x][0][0][1] = f[x][0][1][1] = F[Fa][1] - min(F[x][0], F[x][1]);
}
void dfs3(int x,int Fa)
{
    for(int j = 1;j <= Log[dep[x]];j ++)
    {
        fa[x][j] = fa[fa[x][j - 1]][j - 1];
        for(int a = 0;a < 2;a ++)
          for(int c = 0;c < 2;c ++)
          {
            f[x][j][a][c] = inf;
            for(int b = 0;b < 2;b ++)
              f[x][j][a][c] = min(f[x][j][a][c], f[x][j - 1][a][b] + f[fa[x][j - 1]][j - 1][b][c]);
          }
    }
    for(int k = last[x];k;k = e[k].next)
      if(e[k].y != Fa)
      {
        int y = e[k].y;
        dfs3(y, x);
      }
}
LL getans(int x,int a,int y,int b)
{
    LL ancx[2] = {inf, inf}, ancy[2] = {inf, inf};
    LL nowx[2] = {inf, inf}, nowy[2] = {inf, inf};
    if(dep[x] < dep[y]) swap(x, y), swap(a, b);
    nowx[a] = F[x][a]; nowy[b] = F[y][b];
    for(int j = Log[dep[x]];j >= 0;j --)
      if(dep[fa[x][j]] >= dep[y])
      {
        ancx[0] = ancx[1] = inf;
        for(int fx = 0;fx < 2;fx ++)
          for(int fanc = 0;fanc < 2;fanc ++)
            ancx[fanc] = min(ancx[fanc], nowx[fx] + f[x][j][fx][fanc]);
        nowx[0] = ancx[0]; nowx[1] = ancx[1]; x = fa[x][j];
      }
    if(x == y) return nowx[b] + G[y][b];
    for(int j = Log[dep[x]];j >= 0;j --)
      if(fa[x][j] != fa[y][j])
      {
        ancx[0] = ancx[1] = ancy[0] = ancy[1] = inf;
        for(int fx = 0;fx < 2;fx ++)
          for(int fanc = 0;fanc < 2;fanc ++)
          {
            ancx[fanc] = min(ancx[fanc], nowx[fx] + f[x][j][fx][fanc]);
            ancy[fanc] = min(ancy[fanc], nowy[fx] + f[y][j][fx][fanc]);
          }
          nowx[0] = ancx[0]; nowx[1] = ancx[1]; nowy[0] = ancy[0]; nowy[1] = ancy[1];
          x = fa[x][j]; y = fa[y][j];
      }
    int LCA = fa[x][0]; 
    LL Ans = G[LCA][0] + F[LCA][0] - F[x][1] - F[y][1] + nowx[1] + nowy[1];
    Ans=min(Ans,G[LCA][1]+F[LCA][1]-min(F[x][0],F[x][1])-min(F[y][0],F[y][1])+min(nowx[0],nowx[1])+min(nowy[0],nowy[1]));
    return Ans;
}
int main()
{
    dep[0] = - 1;
    n = read(); m = read(); char type[2]; cin >> type;
    for(int i = 1;i <= n;i ++) Log[i] = Log[i / 2] + 1, val[i] = read();
    for(int i = 1;i <= n - 1;i ++)
    {
        int u = read(), v = read();
        e[++ p].Add_edge(u, v);
        e[++ p].Add_edge(v, u);
    }
    dfs1(1, 0); 
    dfs2(1, 0); 
    dfs3(1, 0);
    for(int i = 1;i <= m;i ++)
    {
        int a = read(),x = read(),b = read(),y = read();
        ans = getans(a, x, b, y);
        if(ans < inf) printf("%lld\n", ans);
        else printf("-1\n");
    }
}

猜你喜欢

转载自www.cnblogs.com/dwqhca/p/11256436.html