2018.11.19【NOIP2018】【洛谷P5024】保卫王国(LCT)

传送门


解析:

真的令人窒息今年这道题。。。

思路:

首先我们要先会O(nm)O(nm)O(nm)DPDPDP做法。

考虑每个位置DP出选与不选在的最小代价。
显然随手就转移了。

然后限制就是把所有限制不能取的方案的代价设置成INFINFINF,然后就可以DP乱搞了。

然后我们发现每次修改的只有点到rootrootroot的路径上的决策。

于是就可以LCTLCTLCT维护每条链上的决策了,用矩阵进行决策的转移就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc put_char
#define cs const

namespace IO{
    cs int Rlen=1<<18|1;
    char buf[Rlen],*p1,*p2;
    char obuf[Rlen],*p3=obuf,*p4=obuf+Rlen;
    
    inline char get_char(){
        return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
    }	

    inline int getint(){
        re int num;
        re char c;
        while(!isdigit(c=gc()));num=c^48;
        while(isdigit(c=gc()))num=num*10+(c^48);
        return num;
    }
    
    inline void put_char(cs char &c){
        *p3++=c;
        if(p3==p4)fwrite(obuf,1,Rlen,stdout),p3=obuf;
    }
    
    inline void outint(ll a){
        static char ch[23];
        if(a==0)pc('0');
        if(a<0)pc('-'),a=-a;
        while(a)ch[++ch[0]]=a-a/10*10,a/=10;
        while(ch[0])pc(ch[ch[0]--]^48);
    }
    
    inline void FLUSH(){
        if(p3==obuf)return ;
        fwrite(obuf,1,p3-obuf,stdout);
        p3=obuf;
    }
}
using namespace IO;
cs int N=100005;
cs ll INF=0x3f3f3f3f3f3f3f;

vector<int> edge[N];
inline void addedge(int u,int v){
    edge[u].push_back(v);
    edge[v].push_back(u);
}

struct matrix{
    ll a[2][2];
    matrix(ll v=INF){a[1][1]=a[1][0]=a[0][0]=a[0][1]=v;}
    friend matrix operator*(cs matrix &A,cs matrix &B){
        matrix C;
        for(int re i=0;i<=1;++i)
        for(int re j=0;j<=1;++j)
        for(int re k=0;k<=1;++k)
        C.a[i][k]=min(C.a[i][k],A.a[i][j]+B.a[j][k]);
        return C;
    }
};

typedef struct splay_node *point;
struct splay_node{
    point son[2],fa;
    matrix a;ll val[2];
    
    #define lc son[0]
    #define rc son[1]
    
    inline void pushup(){
        a.a[1][0]=a.a[1][1]=val[1];
        a.a[0][1]=val[0];
        a.a[0][0]=INF;
        if(rc)a=a*rc->a;
        if(lc)a=lc->a*a;
    }
    
    inline void modify(point b,int sign){
        ll nothave=min(b->a.a[1][0],b->a.a[1][1]);
        ll have=min(nothave,min(b->a.a[0][0],b->a.a[0][1]));
        val[0]+=sign*nothave;
        val[1]+=sign*have;
    }
    
    inline bool isroot()cs{return !fa||(fa->lc!=this&&fa->rc!=this);}
    inline bool which()cs{return fa->rc==this;}
};

int val[N];
struct Link_Cut_Tree{
    splay_node t[N];
    
    void dfs(int u,int fa){
        re point now=&t[u];
        now->val[0]=0;
        now->val[1]=val[u];
        for(int re e=0;e<edge[u].size();++e){
            int v=edge[u][e];
            if(v==fa)continue;
            dfs(v,u);
        }
        now->pushup();
        if(fa){
            t[fa].modify(now,1);
            now->fa=&t[fa];
        }
    }
    inline void init(){dfs(1,0);}
    
    inline void Rotate(point now){
        re point Fa=now->fa,FA=Fa->fa;
        re bool pos=now->which();
        if(FA&&!Fa->isroot())FA->son[Fa->which()]=now;
        Fa->son[pos]=now->son[!pos];
        if(Fa->son[pos])Fa->son[pos]->fa=Fa;
        now->son[!pos]=Fa;
        Fa->fa=now;
        now->fa=FA;
        Fa->pushup();
        now->pushup();
    }
    
    inline void Splay(point now){
        for(point re Fa=now->fa;!now->isroot();Rotate(now),Fa=now->fa)
        if(!Fa->isroot())Rotate(now->which()==Fa->which()?Fa:now);
    }
    
    inline void access(point now){
        for(point re son=NULL;now;son=now,now=now->fa){
            Splay(now);
            if(now->rc)now->modify(now->rc,1);
            now->rc=son;
            if(now->rc)now->modify(now->rc,-1);
            now->pushup();
        }
    }
    
    inline void modify(int pos,bool f,ll add){
        point now=&t[pos];
        access(now);
        Splay(now);
        now->val[!f]+=add;
        now->pushup();
    }
    
    inline ll query(){
        re point now=&t[1];
        Splay(now);
        ll ans=min(min(now->a.a[1][0],now->a.a[1][1]),min(now->a.a[0][0],now->a.a[0][1]));
        return ans>=INF?-1:ans;
    }
}LCT;

int n,m;
signed main(){
    n=getint();
    m=getint();
    getint();
    for(int re i=1;i<=n;++i){
        val[i]=getint();
    }
    for(int re i=1;i<n;++i){
        re int u=getint(),v=getint();
        addedge(u,v);
    }
    LCT.init();
    while(m--){
        re int u=getint(),tu=getint();
        re int v=getint(),tv=getint();
        LCT.modify(v,tv,INF);
        LCT.modify(u,tu,INF);
        outint(LCT.query());pc('\n');
        LCT.modify(u,tu,-INF);
        LCT.modify(v,tv,-INF);
    }
    FLUSH();
    return 0;
}

转载于:https://www.cnblogs.com/zxyoi/p/10047087.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值