【NOIP2018】DAY2T3——保卫王国(DDP+LCT)

描述
Z 国有n座城市,n − 1条双向道路,每条双向道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。
Z 国的国防部长小 Z 要在城市中驻扎军队。驻扎军队需要满足如下几个条件:
⚫ 一座城市可以驻扎一支军队,也可以不驻扎军队。
⚫ 由道路直接连接的两座城市中至少要有一座城市驻扎军队。
⚫ 在城市里驻扎军队会产生花费,在编号为i的城市中驻扎军队的花费是pi。
小 Z 很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小 Z 提出了m个要求,每个要求规定了其中两座城市是否驻扎军队。小 Z 需要针对每个要求逐一给出回答。具体而言,如果国王提出的第j个要求能够满足上述驻扎条件(不需要考虑第 j 个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第j个要求无法满足,则需要输出-1 (1 ≤ j ≤ m)。现在请你来帮助小 Z。
输入
第 1 行包含两个正整数𝑛, 𝑚和一个字符串𝑡𝑦𝑝𝑒,分别表示城市数、要求数和数据类型。𝑡𝑦𝑝𝑒是一个由大写字母 A,B 或 C 和一个数字 1,2,3 组成的字符串。它可以帮助你获得部分分。你可能不需要用到这个参数。这个参数的含义在【数据规模与约定】中有具体的描述。
第2行n个整数pi,表示编号i的城市中驻扎军队的花费。
接下来n − 1行,每行两个正整数u, v,表示有一条u到v的双向道路。
接下来m行,第j行四个整数a, x, b, y(a ≠ b),表示第j个要求是在城市a驻扎x支军队,在城市b驻扎y支军队。其中,x 、 y 的取值只有 0 或 1:若 x 为 0,表示城市 a 不得驻扎军队,若 x 为 1,表示城市 a 必须驻扎军队;若 y 为 0,表示城市 b 不得驻扎军队,若 y 为 1,表示城市 b 必须驻扎军队。
输入文件中每一行相邻的两个数据之间均用一个空格分隔。
输出
输出共m行,每行包含 1 个整数,第j行表示在满足国王第j个要求时的最小开销,如果无法满足国王的第j个要求,则该行输出-1。
样例输入
5 3 C3
2 4 1 3 9
1 5
5 2
5 3
3 4
1 0 3 0
2 1 3 1
1 0 5 0
样例输出
12
7
-1
提示
【样例解释】
对于第一个要求,在 4 号和 5 号城市驻扎军队时开销最小。
对于第二个要求,在 1 号、2 号、3 号城市驻扎军队时开销最小。
第三个要求是无法满足的,因为在 1 号、5 号城市都不驻扎军队就意味着由道路直接连接的两座城市中都没有驻扎军队。
在这里插入图片描述
数据类型的含义:
A:城市i与城市i + 1直接相连。
B:任意城市与城市 1 的距离不超过 100(距离定义为最短路径上边的数量),即如果这棵树以 1 号城市为根,深度不超过 100。
C:在树的形态上无特殊约束。
1:询问时保证a = 1, x = 1,即要求在城市 1 驻军。对b, y没有限制。
2:询问时保证a, b是相邻的(由一条道路直接连通)
3:在询问上无特殊约束。

话说动态dpdp是去年WCWC才出来的新东西啊

CCFCCF这么毒瘤的么

如果没有修改就是没有上司的舞会

首先O(mn)O(mn)的做法应该都会吧:

对于一个点的必须选(不选),把相对的另一种(不选/选)设为INFINF

然后再从这个点往上跑一次

然后我们发现B类数据给了一个提示:树高?lognlog_n?SplaySplay

我们可以利用SplaySplay来保证lognlog_n复杂度的单次操作

考虑一下每个点的dp转移,我们可以发现其实这是一个222*2的矩阵的转移(点u的选和不选以及儿子v的选还是不选),转移的时候取个minmin,很像矩阵乘法

考虑到每个点修改其实是把他到根的链给更改了,所以我们对于每个点记录一个gg矩阵表示原来每个点的dp的值,每个点每次就可以O(1)O(1)更新了

复杂度O(234(n+m)logn)O(2^3*4*(n+m)log_n)

所以满打满算上界刚好1e81e8

再加上常数1s1s不开O2O2很难跑过去,不过2s2s绰绰有余了

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
const ll inf=1e13;
inline int read(){
    char ch=getchar();
    int res=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return res;
}
const int N=100005;
#define min(a,b) (a<b)?(a):(b)
#define max(a,b) (a>b)?(a):(b)
struct matrix{
    ll a[2][2];
    matrix(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=inf;}
    friend matrix operator *(const matrix&a,const matrix&b){
        matrix c;
        for(re int i=0;i<2;++i)
            for(re int k=0;k<2;++k){
                for(re int j=0;j<2;++j){
                    c.a[i][j]=min(c.a[i][j],a.a[i][k]+b.a[k][j]);
                }
            }
        return c;
    }
};
int adj[N],nxt[N<<1],to[N<<1],cnt,n,m,ch[N][2],fa[N],pfa[N];
string ccf;
matrix g[N],mul[N];
ll f[N][2],val[N];
inline void addedge(int u,int v){
    nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
}
inline void dfs(int u,int fat){
    f[u][0]=0,f[u][1]=val[u],pfa[u]=fat;
    for(re int e=adj[u];e;e=nxt[e]){
        int v=to[e];
        if(v==fat)continue;
        dfs(v,u);
        f[u][0]+=f[v][1];
        f[u][1]+=min(f[v][0],f[v][1]);
    }
}
inline void pushup(int u){
    if(ch[u][0])mul[u]=mul[ch[u][0]]*g[u];else mul[u]=g[u];
    if(ch[u][1])mul[u]=mul[u]*mul[ch[u][1]];
}
inline bool hvroot(int u){
    return ch[fa[u]][0]==u||ch[fa[u]][1]==u;
}
inline void rotate(int v){
    int u=fa[v],z=fa[u],whic=(ch[u][1]==v),w=ch[v][whic^1];
    if(hvroot(u))ch[z][ch[z][1]==u]=v;ch[v][whic^1]=u;ch[u][whic]=w;
    if(w)fa[w]=u;fa[u]=v,fa[v]=z,pushup(u),pushup(v);
}
inline void splay(int v){
    while(hvroot(v)){
        int u=fa[v],z=fa[u];
        if(hvroot(u))rotate((ch[u][0]==v)^(ch[z][0]==u)?v:u);
        rotate(v);
    }
}
inline void access(int u){
    for(re int v=0;u;v=u,u=fa[v]){
        splay(u);
        matrix ret=mul[ch[u][1]];
        if(ch[u][1])g[u].a[0][1]+=ret.a[1][1],g[u].a[1][0]+=min(ret.a[1][1],ret.a[0][1]);
        if(v)g[u].a[0][1]-=mul[v].a[1][1],g[u].a[1][0]-=min(mul[v].a[1][1],mul[v].a[0][1]);	
        ch[u][1]=v;		
        g[u].a[1][1]=g[u].a[1][0];
        pushup(u);
    }
}
inline void change(int u,ll k){
    access(u),splay(u);
    g[u].a[1][0]+=k;g[u].a[1][1]+=k;
    pushup(u);
}
inline ll query(){
    splay(1);
    return min(mul[1].a[0][1],mul[1].a[1][1]);
}
inline void solve(int u,int x,int v,int y){
    if(x==0&&y==0&&(pfa[u]==v||pfa[v]==u)){puts("-1");return;}
    ll pasu=val[u],pasv=val[v];
    change(u,x?-inf:inf),change(v,y?-inf:inf);
    cout<<query()+inf*(x+y)<<'\n';
    change(u,x?inf:-inf),change(v,y?inf:-inf);
}
signed main(){
    n=read(),m=read();cin>>ccf;
    for(re int i=1;i<=n;++i)val[i]=read();
    for(re int i=1;i<n;++i){
        int u=read(),v=read();
        addedge(u,v);addedge(v,u);
    }
    dfs(1,0);
    for(re int i=1;i<=n;++i){
        fa[i]=pfa[i];ll x=0,y=val[i];
        for(re int e=adj[i],v;e;e=nxt[e]){
            v=to[e];
            if(v==pfa[i])continue;
            x+=f[v][1],y+=min(f[v][1],f[v][0]);
        }
        g[i].a[0][0]=inf,g[i].a[0][1]=x,g[i].a[1][0]=g[i].a[1][1]=y,pushup(i);
    }
    int u,x,v,y;
    while(m--){
        u=read(),x=read(),v=read(),y=read();
        solve(u,x,v,y);
    }
    return 0;
}	

据说也可以直接倍增优化dp,复杂度也是一样的,但是我不想写了

最后
推广一下另外几篇题解:
DAY1T1:铺设道路:(并查集??)
DAY1T2:货币系统:(完全背包/搜索)
DAY1T3:赛道修建:(二分答案+贪心策略)
DAY2T1:旅行:(基环树搜索)
DAY2T2:填数游戏:(暴力搜索找规律)
DAY2T3:保卫王国:(动态dp+Splay)

posted @ 2018-11-26 00:34  Stargazer_cykoi  阅读(279)  评论(0编辑  收藏  举报