NOIP2018 保卫王国【动态DP(LCT版)】

题目描述:

n个点的树,每个点选的代价为val[i],m个询问,问确定某两个点选或不选后树的最小点覆盖。n,m<=100000

题目分析:

最小点覆盖,设 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示 i i i点不选/选时子树的最小代价,转移为:
{ f [ u ] [ 0 ] = ∑ v f [ v ] [ 1 ] f [ u ] [ 1 ] = v a l [ u ] + ∑ v min ⁡ ( f [ v ] [ 0 ] , f [ v ] [ 1 ] ) \begin{cases}f[u][0]=\sum_vf[v][1]\\f[u][1]=val[u]+\sum_v\min(f[v][0],f[v][1])\end{cases} {f[u][0]=vf[v][1]f[u][1]=val[u]+vmin(f[v][0],f[v][1])
然后剩下就是动态DP的事情了,可以类比动态DP【模板】
如果强制要求选1就把 f [ 1 ] + = − I N F f[1]+=-INF f[1]+=INF,最后再加回来,如果要求选0就把 f [ 1 ] + = I N F f[1]+=INF f[1]+=INF就是正常的动态DP了。

对于此题为了方便使链末尾的点的转移矩阵恰好对应它的 f f f,可以这样设置:
[ f [ 1 ] f [ 0 ] ] = [ f ′ [ 1 ] f ′ [ 1 ] f ′ [ 0 ] I N F ] ∗ [ f s o n [ 1 ] f s o n [ 0 ] ] \begin{bmatrix}f[1]\\f[0]\end{bmatrix}=\begin{bmatrix}f'[1] & f'[1]\\f'[0] & INF\end{bmatrix}*\begin{bmatrix}f_{son}[1]\\f_{son}[0]\end{bmatrix} [f[1]f[0]]=[f[1]f[0]f[1]INF][fson[1]fson[0]]
f ′ [ 0 / 1 ] f'[0/1] f[0/1]表示用只用轻儿子转移时的 f f f值。

如果将1,0反过来的话就会发现转移矩阵变成了 [ I N F f ′ [ 0 ] f ′ [ 1 ] f ′ [ 1 ] ] \begin{bmatrix}INF & f'[0]\\f'[1] & f'[1]\end{bmatrix} [INFf[1]f[0]f[1]],此时链末尾的左半部分就不与它的f矩阵等价了,但是你可以给末尾乘上一个 [ I N F 0 ] \begin{bmatrix}INF\\0\end{bmatrix} [INF0],这样它就等价了,你会发现这时转移矩阵的乘积(未乘0矩阵)的右半部分才是我们想要的 f f f矩阵。



LCT一开始所有儿子都是轻儿子。矩阵拆开写可以优化常数。

Code:

#include
#define maxn 100005
#define LL long long
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
    char c;while(!isdigit(c=getc()));
    for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
inline void write(LL x){
    if(x>=10) write(x/10);
    putchar(x%10+48);
}
const LL inf = 1ll<<50;
struct Mat{
    LL s[2][2];
    Mat(){s[0][0]=s[0][1]=s[1][0]=s[1][1]=inf;}
    void init(LL *f){s[0][0]=s[0][1]=f[1],s[1][0]=f[0],s[1][1]=inf;}
    LL Min(){return min(s[0][0],s[1][0]);}
    Mat operator * (const Mat &B)const{
        Mat ret;
        ret.s[0][0]=min(s[0][0]+B.s[0][0],s[0][1]+B.s[1][0]);
        ret.s[0][1]=min(s[0][0]+B.s[0][1],s[0][1]+B.s[1][1]);
        ret.s[1][0]=min(s[1][0]+B.s[0][0],s[1][1]+B.s[1][0]);
        ret.s[1][1]=min(s[1][0]+B.s[0][1],s[1][1]+B.s[1][1]);
        return ret;
    }
};
namespace LCT{
    int ch[maxn][2],fa[maxn];
    LL f[maxn][2]; Mat g[maxn];
    #define il inline
    #define pa fa[x]
    il bool isr(int x){return ch[pa][0]!=x&&ch[pa][1]!=x;}
    il bool isc(int x){return ch[pa][1]==x;}
    il void upd(int x){g[x].init(f[x]),g[x]=g[ch[x][0]]*g[x]*g[ch[x][1]];}
    il void rot(int x){
        int y=fa[x],z=fa[y],c=isc(x);
        !isr(y)&&(ch[z][isc(y)]=x);
        (ch[y][c]=ch[x][!c])&&(fa[ch[y][c]]=y);
        fa[ch[x][!c]=y]=x,fa[x]=z;
        upd(y),upd(x);
    }
    il void splay(int x){
        for(;!isr(x);rot(x))
            if(!isr(pa)) rot(isc(pa)==isc(x)?pa:x);
    }
    il void access(int x){
        for(int y=0;x;x=fa[y=x]){
            splay(x);
            f[x][0]+=g[ch[x][1]].s[0][0]-g[y].s[0][0];
            f[x][1]+=g[ch[x][1]].Min()-g[y].Min();
            ch[x][1]=y,upd(x);
        }
    }
    il void modify(int x,bool v){access(x),splay(x),f[x][1]+=v?-inf:inf,upd(x);}
}
using namespace LCT;
int n,m,val[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
void dfs(int u,int ff){
    fa[u]=ff,f[u][1]=val[u];
    for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
        dfs(v,u);
        f[u][0]+=f[v][1];
        f[u][1]+=min(f[v][0],f[v][1]);
    }
    g[u].init(f[u]);
}
int main()
{
    g[0].s[0][0]=g[0].s[1][1]=0;
    int a,x,b,y;
    read(n),read(m),read(x);
    for(int i=1;i<=n;i++) read(val[i]);
    for(int i=1;i<n;i++) read(x),read(y),line(x,y),line(y,x);
    dfs(1,0);
    while(m--){
        read(a),read(x),read(b),read(y);
        modify(a,x),modify(b,y),splay(1);
        LL ans=g[1].Min()+(x+y)*inf;
        if(ans>=inf) puts("-1");
        else write(ans),putchar('\n');
        modify(a,!x),modify(b,!y);
    }
}

你可能感兴趣的:(动态DP)