1. 程式人生 > >luogu_P3345[zjoi2015]幻想鄉戰略遊戲

luogu_P3345[zjoi2015]幻想鄉戰略遊戲

傳送門

Description

傲嬌少女幽香正在玩一個非常有趣的戰略類遊戲,本來這個遊戲的地圖其實還不算太大,幽香還能管得過來,但是不知道為什麼現在的網遊廠商把遊戲的地圖越做越大,以至於幽香一眼根本看不過來,更別說和別人打仗了。

在打仗之前,幽香現在面臨一個非常基本的管理問題需要解決。 整個地圖是一個樹結構,一共有\(n\)塊空地,這些空地被\(n-1\)條帶權邊連線起來,使得每兩個點之間有一條唯一的路徑將它們連線起來。

在遊戲中,幽香可能在空地上增加或者減少一些軍隊。同時,幽香可以在一個空地上放置一個補給站。 如果補給站在點\(u\)上,並且空地v上有\(dv\)

個單位的軍隊,那麼幽香每天就要花費\(dv*dist(u,v)\)的金錢來補給這些軍隊。

由於幽香需要補給所有的軍隊,因此幽香總共就要花費為\(\sum Dv*dist(u,v)\) (\(1\leq V \leq N\))的代價。其中\(dist(u,v)\)表示u個v在樹上的距離(唯一路徑的權和)。

因為遊戲的規定,幽香只能選擇一個空地作為補給站。在遊戲的過程中,幽香可能會在某些空地上製造一些軍隊,也可能會減少某些空地上的軍隊,進行了這樣的操作以後,出於經濟上的考慮,幽香往往可以移動他的補給站從而省一些錢。

但是由於這個遊戲的地圖是在太大了,幽香無法輕易的進行最優的安排,你能幫幫她嗎? 你可以假定一開始所有空地上都沒有軍隊。

Solution

題意:樹上點權修改,詢問帶權重心

大大加深了對點分樹的理解?

首先,點分樹有個性質:點分樹上的兩點的lca在原樹的兩點路徑上,這樣,我們通過記錄點分樹上一點到它的祖先的在原樹上的距離,就可以得到任意兩點在原樹上的距離啦

帶權重心是什麼咧?

可以當做它是這樣一個點,已這個點為根的每個子樹的權重都不超過原樹的總size。

為什麼?假如找到了這個點,那麼無論把它移動進入哪個子樹,代價都會變得更大,讀者可以自行證明。

而我們用上面的做法,就可以得到樹的帶權重心。

對於這道題:

  • 先預處理出點分樹,記錄下每個點在點分樹上的孩子和祖先以及和每個祖先的實際距離(便於以後查詢)

  • 記錄下點分樹上每個節點,在原樹上所代表的聯通塊的根節點\(HR\)(這裡指的是深度最小的那個點)

    為什麼要記錄這個東西咧?

    因為我們在尋找帶權重心的時候,每進入一個點,就要更改某些點的權重,因為要算上除了這個聯通塊外的權重,而需要更改的點就是這個\(HR\),可以理解為,它是這個聯通塊的邊界

    當然找到重心後,你得把你做的更改全都改回來

  • 我們考慮每次更改會有什麼影響,首先,總權重增加\(e\),點分樹上的相關節點的總權重也會發生變化,還有就是要即使更新相關節點的代價和,具體的,加上\(e*dis\),這裡的相關節點指的是修改節點的祖先節點(我們顯然只需要記錄它對祖先的貢獻,根據點分樹的性質)

  • 查詢就是列舉這個點與重心的在點分樹上的\(lca\),統計代價即可

因為點分樹的深度不超過\(\log n\),所以總複雜度是\(O(n log^2 n)\)


Code 

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define MN 100005
#define reg register
int n,m,en,hr[MN];
struct edge{int to,w,nex;}e[MN<<1];
inline void ins(int f,int t,int w)
{
    e[++en]=(edge){t,w,hr[f]};hr[f]=en;
    e[++en]=(edge){f,w,hr[t]};hr[t]=en;
}
bool vis[MN];
int rt,sm,siz[MN],dep[MN];
ll vsum,w[MN],val[MN];
struct Node{int id;ll dis;};
std::vector<Node> fa[MN],s[MN];
int HR[MN];
void pre_dfs(int x,int f)
{
    reg int i;siz[x]=1;
    for(i=hr[x];i;i=e[i].nex)if(!vis[e[i].to]&&(f^e[i].to))
        pre_dfs(e[i].to,x),siz[x]+=siz[e[i].to];
}
void getroot(int x,int f)
{
    reg int i;rt=x;
    for(i=hr[x];i;i=e[i].nex)
    if(!vis[e[i].to]&&(f^e[i].to)&&(siz[e[i].to]<<1)>=sm){getroot(e[i].to,x);break;}
}
void dfs(int x,int f,int ro)
{
    fa[x].push_back((Node){ro,dep[x]});
    reg int i;
    for(i=hr[x];i;i=e[i].nex)if(!vis[e[i].to]&&(f^e[i].to))
        dep[e[i].to]=dep[x]+e[i].w,dfs(e[i].to,x,ro);
}
inline void pre_work(int x=1,int f=0)
{
    pre_dfs(x,f);
    sm=siz[x];getroot(x,f);vis[rt]=true;
    reg int i,j=rt;
    s[f].push_back((Node){j,0LL});fa[j].push_back((Node){j,0LL});
    HR[j]=x;
    for(i=hr[rt];i;i=e[i].nex)
        if(!vis[e[i].to]){dep[e[i].to]=e[i].w;dfs(e[i].to,j,j);pre_work(e[i].to,j);}
    rt=j;
}
inline void Modify(int x,int y)
{
    vsum+=y;val[x]+=y;reg int i,j=x,f,k;
    for(i=fa[x].size()-1;~i;--i) w[fa[x][i].id]+=y;
    for(i=fa[x].size()-2;~i;--i)
        for(j=s[fa[x][i].id].size()-1;~j;--j)
        if(s[fa[x][i].id][j].id==fa[x][i+1].id){s[fa[x][i].id][j].dis+=y*fa[x][i].dis;break;}
}
inline void upd(int x,int y){for(reg int i=fa[x].size()-1;~i;--i)w[fa[x][i].id]+=y;}
inline int Find(int x)
{
    reg int ret=x,i,tmp;
    for(i=s[x].size()-1;~i;--i)
        if(w[s[x][i].id]*2>=vsum)
        {
            tmp=w[x]-w[s[x][i].id];
            upd(HR[s[x][i].id],tmp);ret=Find(s[x][i].id);upd(HR[s[x][i].id],-tmp);
            break;
        }
    return ret;
}
inline ll Query(int x)
{
    reg ll ret=0;
    reg int i,j,las=0;
    for(i=fa[x].size()-1;~i;las=fa[x][i].id,--i)
    {
        ret+=fa[x][i].dis*val[fa[x][i].id];
        for(j=s[fa[x][i].id].size()-1;~j;--j)
            if(s[fa[x][i].id][j].id^las)
                ret+=fa[x][i].dis*w[s[fa[x][i].id][j].id]+s[fa[x][i].id][j].dis;
    }
    return ret;
}
int main()
{
    n=read();m=read();
    register int i,x,y;
    for(i=1;i<n;++i) x=read(),y=read(),ins(x,y,read());
    pre_work();
    while(m--)
    {
        x=read();y=read();Modify(x,y);
        printf("%lld\n",Query(Find(rt)));
    }
    return 0;
}



Blog來自PaperCloud,未經允許,請勿轉載,TKS!