您的位置:首页 > 其它

[BZOJ3924][ZJOI2015]幻想乡战略游戏-动态树分治

2017-10-03 23:42 330 查看

幻想乡战略游戏

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)。

数据保证任何时刻每个点上的军队数量都是非负的。

1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5

对于所有数据,这个树上所有点的度数都不超过20

N,Q>=1

Output

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

Sample Input

10 5

1 2 1

2 3 1

2 4 1

1 5 1

2 6 1

2 7 1

5 8 1

7 9 1

1 10 1

3 1

2 1

8 1

3 1

4 1

Sample Output

0

1

4

5

6

样例有误,输入边的地方有两行边权和点的编号中间没加空格……

话说一遍过还真是舒服啊

难道咱有写工业题的潜质?

(今早NOIP模拟题T1誓死不用STL写出280+行,STL党仅需120+行(虽然还是A了))

思路:

如果可以动态维护每个点的答案值,那么就可以很稳地做出此题。

可以发现,如果咱记录点u的子树点权和sumd[u],那么如果u到当前要计算值的点需要经过其父亲边向父亲走,那么这条父亲边对答案的贡献便是其权值*sumd[u]。

那么考虑维护sumd[u]来求答案。

考虑到出题人必须要足够坑的基本素质,每次如果暴力修改,复杂度会是是O(n)的,单次询问由于需要计算经过的点,复杂度也是O(n)的。

那这就会很尴尬地TLE了……

考虑使用动态点分治优化树的结构——建出树高仅为log的点分树。

所谓点分树,就是在点分治时把各个分治中心连在一起得到的一棵树~

那么可以发现之前的式子无法直接使用边权,而且还需要减去与父亲节点之间相冲突的子树部分。

那就维护一个dis,代表以u为根的子树中,所有节点到u的父亲所需的价值和,再维护一个fadis,代表以u为根的子树中,所有节点到u所需的价值和。

(名字很奇怪对吧只是咱写代码时写反了而已)

这两个值可以在修改操作时维护一下。

那么这就可以用加减法排除掉重复子树的影响了~~

修改直接暴力从被修改点开始往上一个个暴力跳父亲,反正点分树深度log~

至于查询,可以发现(显然是错的,显然可以随手卡掉)新的答案位置在原树上不会距离上一个答案位置太远。

那就在原树上暴力跳可好??反正单点价值查询只是log~~

(10s时限只用了4s证明效果还是不错的)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

typedef long long ll;

const ll N=1e5+9;
const ll K=22;

inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x*f;
}

inline void chkmax(ll &a,ll b){if(a<b)a=b;}
inline ll minn(ll a,ll b){if(a<b)return a;return b;}

ll n,q,lans=1;
ll d
;

namespace tree
{
ll to[N<<1],nxt[N<<1],w[N<<1],beg
,tot=1;
ll id
,dfn,st[N<<1][K],dep
,logs[N<<1];
ll fa
,sumd
,dis
,fadis
;
bool ban[N<<1];

inline void add(int u,int v,int c)
{
to[++tot]=v;
nxt[tot]=beg[u];
w[tot]=c;
beg[u]=tot;
}

inline void dfs(ll u,ll fat=0)
{
st[++dfn][0]=dep[u];
id[u]=dfn;
for(ll i=beg[u],v;i;i=nxt[i])
if((v=to[i])!=fat)
{
dep[v]=dep[u]+w[i];
dfs(v,u);
st[++dfn][0]=dep[u];
}
}

inline void init()
{
for(ll i=2;i<=dfn;i++)
logs[i]=logs[i>>1]+1;
for(ll i=1;i<=logs[dfn];i++)
for(ll j=1;j+(1<<i)-1<=dfn;j++)
st[j][i]=minn(st[j][i-1],st[j+(1<<i-1)][i-1]);
}

inline ll dist(ll u,ll v)
{
ll ret=dep[u]+dep[v];
u=id[u],v=id[v];
if(u>v)swap(u,v);
ll dis=logs[v-u+1];
return ret-2*minn(st[u][dis],st[v-(1<<dis)+1][dis]);
}

inline ll get_siz(ll u,ll fa=0)
{
ll siz=1;
for(ll i=beg[u];i;i=nxt[i])
if(!ban[i] && to[i]!=fa)
siz+=get_siz(to[i],u);
return siz;
}

inline ll get_center(ll u,ll fa,ll totsiz,ll &root)
{
ll mxsiz=0,siz=1;
for(ll i=beg[u],v,tmp;i;i=nxt[i])
if(!ban[i] && (v=to[i])!=fa)
{
siz+=(tmp=get_center(v,u,totsiz,root));
chkmax(mxsiz,tmp);
}
chkmax(mxsiz,totsiz-siz);
if((mxsiz<<1)<=totsiz)
root=u;
return siz;
}

inline ll solve(ll u)
{
ll totsiz=get_siz(u),root;
get_center(u,0,totsiz,root);
sumd[root]=d[root];
for(ll i=beg[root],son;i;i=nxt[i])
if(!ban[i])
{
ban[i]=ban[i^1]=1;
son=solve(to[i]);
fa[son]=root;
sumd[root]+=sumd[son];
}
return root;
}

inline void update(ll u,ll val)
{
sumd[u]+=val;
for(ll i=u;fa[i];i=fa[i])
{
ll diss=dist(fa[i],u);
sumd[fa[i]]+=val;
dis[i]+=val*diss;
fadis[fa[i]]+=val*diss;
}
}

inline ll calc(ll u)
{
ll ret=fadis[u];
for(ll i=u;fa[i];i=fa[i])
{
ret+=(fadis[fa[i]]-dis[i]);
ret+=dist(fa[i],u)*(sumd[fa[i]]-sumd[i]);
}
return ret;
}

inline ll query(ll u)//no fa anymore
{
ll ret=calc(u);
for(ll i=beg[u],v;i;i=nxt[i])
if(calc(v=to[i])<ret)
return query(v);
lans=u;
return ret;
}
}

using namespace tree;

int main()
{
if(fopen("tree.in","r"))
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
}

n=read();
q=read();

for(ll i=1,u,v,c;i<n;i++)
{
u=read();
v=read();
c=read();
add(u,v,c);
add(v,u,c);
}

dfs(1);
init();
solve(1);

for(ll i=1,u,e;i<=q;i++)
{
u=read();
e=read();
update(u,e);
printf("%lld\n",query(lans));
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: