ZJOI幻想鄉戰略遊戲【題解】

前言

動點分思維難度還是高些,不像點分那樣板(這也是爲爲什麼寫題解的原因)。但其實也不難,像我這種動點分學都沒學只知道要建點分樹的蒟蒻都可以 大力 yy一波(爲了研究各種性質搗鼓一個晚上)

題面

題面就不粘了,搞個鏈接

sol

分治樹就是一個數據結構。大部分優秀的數據結構都是帶log的,這是因爲利用了化小子問題或是利用線性來作爲原理支撐。
數據結構的優秀是建立在修改與查詢幾乎相等或者不知道兩者分佈的情況下的。因爲當一者遠小於另一者時,可以採用一者使用O(1),而另一種採用O(n)來運算。
動態點分治一定要想象做一次單一的分治如何處理。
和在一個序列上分治一樣,點分樹實際上是記錄了每一層分治的狀態。分治時,我們是要考慮一半對另一半的貢獻。點分樹也是一樣,先考慮相鄰子樹的貢獻,在考慮與更高級子樹的貢獻,在考慮更更高級的子樹的貢獻。所以這樣導致修改也要按這個順序。
這樣做的好處是能保證自己的修改能影響到每一個節點,每一個節點的修改也能影響到自己。
樹上也具有二分的性質。也就是說,帶權重心與重心相似的性質——對一個節點來說,如果他的兒子作爲關鍵點比他要優,那麼重心一定是往他的兒子方向移動。這個性質使其具有二分的能力。
我們可以藉助點分樹來實現這個log;

代碼

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline char gc(){
    static char buf[1<<6],*p1=buf,*p2=buf;
    return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
inline void read(T&data){
    data=0;
    register char ch=0;register int caa=1;
    while((ch<'0'||ch>'9')&&ch!='-')ch=gc();
    ch=='-'?caa=-1,ch=gc():caa=1;
    while(ch<='9'&&ch>='0'){
        data=(data<<3)+(data<<1)+(ch&15);
        ch=gc();
    }
    data*=caa;
}
const int _ (1e5+1e2),INF (2e9);
struct edge1{
    int nt,to,len;
}e1[_<<1];
int head1[_],lg[_<<1],cnt,dist,n,head2[_],m,st[_<<1][22],dfn[_],size[_],MX,all,root,vis[_],par[_],fdfn[_],app[_],dd;
struct edge2{
    int nt,to1,to2;
}e2[_<<1];
LL tofa[_],own[_],fake_ans,sum[_],ans,dis[_];
inline void add1(register int a,register int b,register int c){e1[++cnt].to=a,e1[cnt].nt=head1[b],e1[cnt].len=c,head1[b]=cnt;}
inline void add2(register int a,register int b,register int c){e2[++cnt].to1=a,e2[cnt].to2=c,e2[cnt].nt=head2[b],head2[b]=cnt;}
void getroot(register int now,register int fa){
    size[now]=1;int mx=0;
    for(register int i=head1[now];i;i=e1[i].nt){
        if(vis[e1[i].to]||e1[i].to==fa)continue;
        getroot(e1[i].to,now),size[now]+=size[e1[i].to],mx=max(mx,size[e1[i].to]);
    }
    mx=max(mx,all-size[now]);
    if(mx<MX){root=now,MX=mx;}
    return;
}
void dfsI(register int now,register int fa){
    dfn[now]=++cnt,fdfn[cnt]=now,st[++dd][0]=cnt,app[now]=dd;
    for(register int i=head1[now];i;i=e1[i].nt){
        if(e1[i].to==fa)continue;
        dis[e1[i].to]=dis[now]+e1[i].len;
        dfsI(e1[i].to,now);
        st[++dd][0]=dfn[now];
    }
}
void divide(register int now){
    vis[now]=1;
    for(register int i=head1[now];i;i=e1[i].nt){
        if(vis[e1[i].to])continue;
        all=size[e1[i].to],MX=INF,
            getroot(e1[i].to,now),
            par[root]=now,
            add2(root,now,e1[i].to),
        divide(root);
    }
}
inline void init(){
    register int us=2*n-1;
    for(register int i=2;i<=us;++i)lg[i]=lg[i>>1]+1;
    for(register int j=1;j<=17;++j)
        for(register int i=1;i<=us&&( i+(1<<(j-1)) )<=us;++i)
            st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
inline LL getdis(register int uu,register int vv){
    register int u=app[uu],v=app[vv];if(u>v)swap(u,v);
    register int lca=fdfn[ min(st[u][lg[v-u+1]],st[v+1-(1<<lg[v-u+1])][lg[v-u+1]]) ];
    return dis[uu]+dis[vv]-2LL*dis[lca];
}
inline void modify(register int now,register int zh){
    sum[now]+=zh;
    for(register int i=par[now],j=now;i;j=i,i=par[i]){
        dist=getdis(now,i);
        sum[i]+=zh,own[i]+=1LL*zh*dist,tofa[j]+=1LL*zh*dist;
    }
}
inline LL solve(register int now){
    register LL ret=own[now];
    for(register int i=par[now],j=now;i;j=i,i=par[i]){
        ret=ret+own[i]-tofa[j],
        ret=ret+(sum[i]-sum[j])*getdis(i,now);
    }
    return ret;
}
void query(register int now){
    ans=solve(now);
    for(register int i=head2[now];i;i=e2[i].nt){
        fake_ans=solve(e2[i].to2);
        if(fake_ans<ans){query(e2[i].to1);return;}
    }
    return;
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("1.out","w",stdout);
    read(n),read(m);
    //cout<<n<<' '<<m;exit(0);
    for(register int i=1,a,b,c;i<n;++i)read(a),read(b),read(c),add1(a,b,c),add1(b,a,c);
    cnt=0,dfsI(1,0),MX=INF,all=n,cnt=0;
    getroot(1,0);
    register int rrtt=root;
    cnt=0,divide(root),init();
    for(register int i=1,x,y;i<=m;++i){
        read(x),read(y);
        modify(x,y),ans=0,query(rrtt);
        printf("%lld\n",ans);
    }
}
發佈了47 篇原創文章 · 獲贊 3 · 訪問量 7082
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章