bzoj 3924: [Zjoi2015]幻想乡战略游戏

3924: [Zjoi2015]幻想乡战略游戏

Time Limit: 100 Sec   Memory Limit: 256 MB
Submit: 630   Solved: 291
[ Submit][ Status][ Discuss]

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

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

Sample Input

10 5
1 2 1
2 3 1
2 4 1
1 5 1
2 61
2 7 1
5 8 1
7 91
1 10 1
3 1
2 1
8 1
3 1
4 1

Sample Output

0
1
4
5
6

HINT

对于所有数据,1<=c<=1000, 0<=|e|<=1000, n<=105, Q<=105

Source


考虑如何在log复杂度内统计单点答案:

       首先建一颗点分树,对于每个重心,我们维护三个数组:
sum[u]表示点分树中以u为根的子树的权值和
dis1[u]表示点分树中以u为根的子树中所有节点到u节点dv*dis的和
dis2[u]表示点分树中以u为根的子树中所有节点到u的父亲节点dv*dis的和
那么对于某一个点u统计答案时,我们暴力爬树高,利用dis2和sum消除掉上一颗子树与当前子树重复的部分加进答案即可。具体的过程就不讨论了,自己yy有利于身心健康。

然后统计答案的时候暴力爬树高,消除子树影响就好了。


最后就是全局最优我们这样搞:

发现对于某个节点u来说最多只有一个节点v的答案比他更优,若没有,他就是最优点。

我原本是每次都从1节点开始找,lca聚聚点拨我说会退化成O(n^2),然后yy了一个每次从上次答案点出发走更优子节点所在子树重心的解法,然后加完从上个答案点出发就试着叫了一发,A掉了,不过最好还是加上走重心。。。。。


get_新套路,记录点分树中子节点关于根节点的父亲节点的信息,以便爬树高时消除影响。


附代码:

/**************************************************************
    Problem: 3924
    User: *******
    Language: C++
    Result: Accepted
    Time:32108 ms
    Memory:60008 kb
****************************************************************/
 
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define N 200010
#define M 400020
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1;char ch;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
int head[N],pos;
struct edge{int to,next,c;}e[M];
void add(int a,int b,int c)
{pos++;e[pos].to=b,e[pos].c=c,e[pos].next=head[a],head[a]=pos;}
void insert(int a,int b,int c){add(a,b,c);add(b,a,c);}
 
int dep[N],dist[N],st[N<<1][22],s[N<<1];
int id[N],top,ip[N];
void dfs(int u,int fa)
{
    s[++top]=u;
    if(!id[u])id[u]=top;
    dep[top]=dep[ip[fa]]+1;ip[u]=top;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa)continue;
        dist[v]=dist[u]+e[i].c;
        dfs(v,u);s[++top]=u;dep[top]=dep[ip[fa]+1];
    }
}
void make()
{
    for(int i=1;i<=top;i++) st[i][0]=i;
    for(int j=1;j<=18;j++)
    for(int i=1;i<=top;i++) if(i+(1<<j)-1<=top)
    {
        int x=st[i][j-1],y=st[i+(1<<j-1)][j-1];
        if(dep[st[i][j-1]]<dep[st[i+(1<<j-1)][j-1]])
            st[i][j]=st[i][j-1];
        else
            st[i][j]=st[i+(1<<j-1)][j-1];
    }
}
int query(int l,int r)
{
    int len=r-l+1,k=0;
    for(k=0;1<<k+1<=len;k++);
    if(dep[st[l][k]]<dep[st[r-(1<<k)+1][k]])
        return st[l][k];
    else
        return st[r-(1<<k)+1][k];
}
int lca(int u,int v)
{
    if(id[u]>id[v]) swap(u,v);
    return s[query(id[u],id[v])];
}
int dis(int x,int y)
{
    int lc=lca(x,y);
    return dist[x]+dist[y]-2*dist[lc];
}
 
int rt,sum,f[N],size[N];bool vis[N];
void find_root(int u,int fa)
{
    size[u]=1,f[u]=0;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa||vis[v])continue;
        find_root(v,u);size[u]+=size[v];
        f[u]=max(f[u],size[v]);
    }f[u]=max(f[u],sum-size[u]);
    if(f[u]<f[rt])rt=u;
}
 
int ret,dv[N],p[N];
ll ans[N],ans1[N],summ[N];
void work(int u)
{
    vis[u]=1;
    summ[u]=ret;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(vis[v])continue;
        rt=0,sum=size[v];
        find_root(v,0);
        p[rt]=u;work(rt);
    }
}
 
ll cal(ll u)
{
    ll ret=ans[u];
    for(int i=u;p[i];i=p[i])
    {
        ll dt=dis(p[i],u);
        ret+=(ans[p[i]]-ans1[i]);
        ret+=dt*(summ[p[i]]-summ[i]);
    }return ret;
}
void updata(int u,int val)
{
    summ[u]+=val;
    for(int i=u;p[i];i=p[i])
    {
        ll di=dis(p[i],u);
        summ[p[i]]+=val;
        ans1[i]+=val*di;
        ans[p[i]]+=val*di;
    }
}
int las=1;
ll query(int u)
{
    ll ka=cal(u);
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        ll tmp=cal(v);
        if(tmp<ka)return query(v);
    }las=u;return ka;
}
int main()
{
//  freopen("in.txt","r",stdin);
//  freopen("my.txt","w",stdout);
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int a=read(),b=read(),c=read();
        add(a,b,c);add(b,a,c);
    }top=0;dfs(1,0);
    make();sum=f[0]=n;find_root(1,0);
    work(rt);
    for(int i=1;i<=m;i++)
    {
        int a=read(),b=read();
        updata(a,b);
        printf("%lld\n",query(las));
    }
}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值