Bzoj3924--Fjoi2015幻想乡战略游戏

Description

 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点u上,并且空地v上有dv个单位的军队,那么幽香每天就要花费dv×dist(u,v)的金钱来补给这些军队。由于幽香需要补给所有的军队,因此幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(唯一路径的权和)。 因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。 

Input

第一行两个数n和Q分别表示树的点数和幽香操作的个数,其中点从1到n标号。 接下来n-1行,每行三个正整数a,b,c,表示a和b之间有一条边权为c的边。 接下来Q行,每行两个数u,e,表示幽香在点u上放了e单位个军队(如果e<0,就相当于是幽香在u上减少了|e|单位个军队,说白了就是du←du+e)。数据保证任何时刻每个点上的军队数量都是非负的。 

Output

 对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。 

 

题解 :

构造点分树,记录每个点管辖子树的大小Vc,这个点管辖子树内所有点到这个点的费用和Cos和这个子树对上一级重心费用的贡献Scos

每次修改直接点分树上修改

查询一个点,如果该点最大子树的大小大于全图一半,就把这个点除了最大子树外的部分贴到那个子树里对应的点上再递归去查询那个子树

如果最大子树也小于全图一半,那么这个点就是当前图的重心,答案就是Cos

原题有限制每个点度数不大于20,如果去掉这个限制可以套个线段树,复杂度多个log

代码 :

#include<bits/stdc++.h>
#define PA pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;

inline int _max(int a,int b) {return a>b?a:b;}

#define MAXN 100005

int n,m;

int head[MAXN],cnt;
struct Edge{
    int next,to,w;
}e[MAXN<<1];
inline void insert(int a,int b,int c) {
    e[++cnt].next=head[a];head[a]=cnt;e[cnt].to=b;e[cnt].w=c;
    e[++cnt].next=head[b];head[b]=cnt;e[cnt].to=a;e[cnt].w=c;
}

bool vis[MAXN];int sz[MAXN],weight[2];
void FindW(int v,int all) {
    vis[v]=1;sz[v]=0;int mx=0;
    for(int i=head[v];i;i=e[i].next) {
        if(!vis[e[i].to]) {
            FindW(e[i].to,all);
            mx=_max(mx,sz[e[i].to]);sz[v]+=sz[e[i].to];
        }
    }
    sz[v]++;mx=_max(mx,all-sz[v]);
    if(weight[0]>mx) weight[0]=mx,weight[1]=v;
    vis[v]=0;
}
int Anc[MAXN][26],Len[MAXN],To[MAXN],Tdis[MAXN];LL Dis[MAXN][26];
void Comp(int v,int anc,int d) {
    vis[v]=1;sz[v]=0;
    Anc[v][++Len[v]]=anc;Dis[v][Len[v]]=d;
    for(int i=head[v];i;i=e[i].next) {
        if(!vis[e[i].to]) {
            Comp(e[i].to,anc,d+e[i].w);
            sz[v]+=sz[e[i].to];
        }
    }
    sz[v]++;vis[v]=0;
}
void Solve(int w) {
    vis[w]=1;if(sz[w]==1) return;
    for(int i=head[w];i;i=e[i].next) {
        if(!vis[e[i].to]) {
            weight[0]=INF;FindW(e[i].to,sz[e[i].to]);
            To[weight[1]]=e[i].to;Tdis[weight[1]]=e[i].w;
            Comp(weight[1],weight[1],0);
            Solve(weight[1]);
        }
    }
}

LL Vc[MAXN],Cos[MAXN],Scos[MAXN],All;
inline void Modify(int x,int y) {
    All+=y;
    for(int i=Len[x];i;i--) {
        Vc[Anc[x][i]]+=y;
        Cos[Anc[x][i]]+=y*Dis[x][i];
        Scos[Anc[x][i]]+=y*Dis[x][i-1];
    }
}
void Change(int x,int sp,LL v,LL c,int fx) {
    for(int i=Len[x];i;i--) {
        if(Anc[x][i]==sp) return;
        Vc[Anc[x][i]]+=v;
        Cos[Anc[x][i]]+=v*(Dis[x][i]+fx)+c;
        Scos[Anc[x][i]]+=v*(Dis[x][i-1]+fx)+c;
    }
}
LL Query(int v,int d) {
    LL ans,go,s=0;
    for(int i=head[v];i;i=e[i].next) 
        if(Anc[e[i].to][d]&&Vc[Anc[e[i].to][d]]>s) s=Vc[Anc[e[i].to][d]],go=Anc[e[i].to][d];
    if((s<<1)>All) {
        LL t=Vc[v]-Vc[go],c=Cos[v]-Scos[go];
        Change(To[go],v,t,c,Tdis[go]);
        ans=Query(go,d+1);
        Change(To[go],v,-t,-c,Tdis[go]);
        return ans;
    }
    else return Cos[v];
}

int main() {
    scanf("%d%d",&n,&m);
    for(int a,b,c,i=1;i<n;i++) {
        scanf("%d%d%d",&a,&b,&c);
        insert(a,b,c);
    }
    weight[0]=INF;FindW(1,n);
    Comp(weight[1],weight[1],0);
    Solve(weight[1]);
    for(int x,y,i=1;i<=m;i++) {
        scanf("%d%d",&x,&y);
        Modify(x,y);
        printf("%lld\n",Query(Anc[1][1],2));
    }
    return 0;
}
 
View Code

 

posted @ 2016-12-23 12:32  ihopenot  阅读(226)  评论(0编辑  收藏  举报