P3345 [ZJOI2015]幻想乡战略游戏

传送门

考虑先随便找一个点作为根,然后再慢慢移动根,这样一步步走到最优的点

设 $sum[x]$ 表示节点 $x$ 的子树的军队数,$len(x,y)$ 表示 $x,y$ 之间边的长度

那么对于根节点 $x$ 的一个儿子 $v$,考虑把儿子搞为根时,代价的改变量

$v$ 的子树内的军队消耗减少,共减少了 $sum[v]\cdot len(x,v)$

$v$ 的子树外的军队消耗增加,即根节点 $x$ 的子树内除了 $v$ 子树的军队消耗增加

代价增加了 $(sum[x]-sum[v])\cdot len(x,y)$

如果儿子比父亲优,那么整理可得 $2sum[v]>sum[x]$,显然满足条件的 $v$ 只有一个

此时如果没有满足的 $v$ ,那么 $x$ 就是最优点,否则最优点在 $x$ 的子树内

如果每次都一个一个儿子跳下去,显然会GG

但是因为最优点在子树内所以可以考虑在点分树上跳

我们需要维护两个东西 : $S[x],Sf[x]$,分别表示节点 $x$的点分树子树到 $x$ 的总代价,节点 $x$ 的点分树子树到 $x$ 在点分树父亲 $Fa[x]$ 的总代价

那么计算一个节点 $x$ 的总消耗就考虑一直往 $Fa$ 跳,每次跳完就考虑这一段产生的代价

设当前跳到了节点 $now$

那么十分显然 $Fa[now]$ 的点分树子树产生的代价为 $S[Fa[now]]-Sf[now]+(sum[Fa[now]]-sum[now])\cdot dis(x,Fa[now])$

($dis(x,y)$表示节点 $x,y$ 在原树上的距离,注意此时 $sum[x]$ 表示节点 $x$ 的点分树子树军队总数)

我们可以用 RMQ 求 LCA 来 $O(1)$ 求出两点间的距离

至于修改操作也在点分树上直接维护就好了

注意$long long$,代码有注释

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
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<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2e5+7,INF=1e9+7;
int fir[N],from[N],to[N],val[N],cntt;
inline void add(int &a,int &b,int &c)
{
    from[++cntt]=fir[a]; fir[a]=cntt;
    to[cntt]=b; val[cntt]=c;
}
int n,m,tot,rt;
int sz[N],mx[N],Fa[N];
vector <int> V[N],G[N];//存点分树
bool vis[N];
void find_rt(int x,int fa)
{
    sz[x]=1; mx[x]=0;
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i]; if(v==fa||vis[v]) continue;
        find_rt(v,x); sz[x]+=sz[v];
        mx[x]=max(mx[x],sz[v]);
    }
    mx[x]=max(mx[x],tot-sz[x]);
    if(mx[x]<mx[rt]) rt=x;
}
void build(int x)//建点分树
{
    vis[x]=1;
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i]; if(vis[v]) continue;
        tot=sz[v]; rt=0; find_rt(v,0);
        V[x].push_back(rt); G[x].push_back(v);
        Fa[rt]=x;  build(rt);
    }
}
int st[N],dfn[N],pos[N],dis[N],Top,dfs_clock,f[N][21],Log[N];//维护RMQ求LCA维护dis
void dfs(int x,int fa)
{
    dfn[x]=++dfs_clock; st[++Top]=x; pos[x]=Top;
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i]; if(v==fa) continue;
        dis[v]=dis[x]+val[i]; dfs(v,x);
        st[++Top]=x;
    }
}
void pre()
{
    Log[0]=-1; for(int i=1;i<=Top;i++) Log[i]=Log[i>>1]+1;
    for(int i=1;i<=Top;i++) f[i][0]=st[i];
    for(int i=1;(1<<i)<=Top;i++)
        for(int j=1;j+(1<<i-1)<=Top;j++)
        {
            if(dfn[f[j][i-1]]<dfn[ f[j+(1<<i-1)][i-1] ]) f[j][i]=f[j][i-1];
            else f[j][i]=f[j+(1<<i-1)][i-1];
        }
}
inline int LCA(int x,int y)
{
    int l=pos[x],r=pos[y]; if(l>r) swap(l,r);
    int k=Log[r-l+1];
    if(dfn[f[l][k]]<dfn[f[r-(1<<k)+1][k]]) return f[l][k];
    return f[r-(1<<k)+1][k];
}
inline int Dis(int x,int y) { return dis[x]+dis[y]-2*dis[LCA(x,y)]; }
ll sum[N],S[N],Sf[N];//注意long long
inline void change(int x,int y)//修改操作
{
    sum[x]+=y;
    for(int now=x;Fa[now];now=Fa[now])//在点分树上跳
    {
        int d=Dis(x,Fa[now]);
        Sf[now]+=1ll*d*y;
        S[Fa[now]]+=1ll*d*y;
        sum[Fa[now]]+=y;
    }
}
inline ll calc(int x)//计算以x为根的花费
{
    ll res=S[x];
    for(int now=x;Fa[now];now=Fa[now])
    {
        int d=Dis(x,Fa[now]);
        res+=S[Fa[now]]-Sf[now]+(sum[Fa[now]]-sum[now])*d;
    }
    return res;
}
ll query(int x)//点分树上暴力dfs找最优解
{
    ll res=calc(x); int len=V[x].size();
    for(int i=0;i<len;i++)
    {
        ll t=calc(G[x][i]);//注意是算G[x][i]
        if(t<res) return query(V[x][i]);//注意是往V[x][i]跳
    }
    return res;
}
int main()
{
    int a,b,c,RT;
    n=read(); m=read();
    for(int i=1;i<n;i++)
    {
        a=read(),b=read(),c=read();
        add(a,b,c); add(b,a,c);
    }
    tot=n; mx[0]=INF;
    find_rt(1,0); RT=rt; build(rt);
    dfs(1,0); pre();
    while(m--)
    {
        a=read(); b=read();
        change(a,b);
        printf("%lld\n",query(RT));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/LLTYYC/p/10337258.html

时间: 2024-10-07 11:58:13

P3345 [ZJOI2015]幻想乡战略游戏的相关文章

luogu P3345 [ZJOI2015]幻想乡战略游戏 |动态点分治

题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来. 在游戏中,幽香可能在空地上增加或者减少一些军队.同时,幽香可以在一个空地上放置一个补给站. 如果补给站在点u上,

luogu P3345 [ZJOI2015]幻想乡战略游戏(点分树)

题意自己看... 思路 没想到今(昨)天刷着刷着点分治的水题,就刷出来了一个点分树... 然后就疯狂地找题解,代码,最后终于把它给弄懂了. 点分树--动态点分治,对于此题来说,我们发现设u为当前的补给站位置,v是它的一个儿子.同时设dis(i,j)为树上i点到j点的距离.sumi为以i为跟的子树中d(也就是军队数)的总量 我们把补给站从u转移到v,答案的变化为dis(u,v)*(sumu-sumv)-dis(u,v)*sumv=dis(u,v)*(sumu-2*sumv) 所以当2*sumv>s

bzoj3924 [Zjoi2015]幻想乡战略游戏 点分树,动态点分

[BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来.在游戏中,幽香可能在空地上增加或者减少一些军

[ZJOI2015]幻想乡战略游戏 解题报告 (动态点分治)

[ZJOI2015]幻想乡战略游戏 题意 有一棵大小为 \(n\) 的带权树, 每个点有一个权值, 权值可以修改 \(q\) 次, 找出一个补给点 \(x\) , 使得 \(\sum_{u \in V} val[u] \times dis(x,u)\) 最小, 并求出这个最小值. 一句话 : 求带权重心 (zsy说的) 附加条件 : 树中所有点的度数不超过 \(20\). 思路 一道你以为复杂度过不了, 但其实是过得了的题. 首先, 我们假定先选了一个点 \(u\) 作为补给点, 我们可以算出它

BZOJ 3924 Zjoi2015 幻想乡战略游戏 动态树分治

题目大意:给定一棵树,每个点有一个点权,多次改变某个点的点权,多次查询带权重心到所有点的带权距离之和 此生无悔入东方,来世愿生幻想乡 首先我们考虑如何计算一个点到所有点的带权距离之和且支持修改 用动态树分治就好了嘛... 每个点记录子树中带权距离之和,以及权值之和,再在每个子树中记录一个需要减掉的版本 然后一直向上扫到根就能统计了 ↑这段话面对会写动态树分治的人,不会的先去切捉迷藏吧 然后就好搞了... 对于分治结构的每一个点,我们枚举它的出边 如果某条出边连向的点的距离之和小于当前点,那么答案

【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态树分治

题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来.在游戏中,幽香可能在空地上增加或者减少一些军队.同时,幽香可以在一个空地上放置一个补给站. 如果补给站在点u上,并

BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)

这种动态点分治嘛,GDKOI时听打到了,也有同学讲到了,所以印象比较深刻也就想出来了,然后就在实现方面卡了好久= = 不得不说CLJ说得真的太简单了,实现方面根本没提. 首先我们可以先用树分治构建出这棵树的分治树,也就是把这棵树的重心作为根节点然后子树为他的子树的重心这样递归下去,然后每个节点存的是其子树的信息. 对于每个节点我们保存这个子树的dv的总和已经把该节点作为点的答案值 这样对于修改能在log n的时间内解决 寻找答案的时候,我们可以发现,如果现在节点的子树dv和*2大于总节点,那么向

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

对于一个点,要求出它到所有点的带权距离和,只需记录下树分治的结构然后查询即可. 修改$O(\log n)$,查询$O(\log n)$. 到所有点带权距离和最小的点显然是这棵树的带权重心. 以1号点为根,考虑一条从父亲x到孩子y的边: 若y子树内权值和>=总权值和-y子树内权值和,即2*y子树内权值和>=总权值和,则重心在y的子树里,否则不在. 所以重心一定是深度最大的满足2*子树内权值和>=总权值和的点. 可以发现重心及其祖先都满足2*子树内权值和>=总权值和,而这些点在DFS序

【BZOJ 3924】[Zjoi2015]幻想乡战略游戏

题目: 题解: 对点分树理解加深了233,膜拜zzh干翻紫荆花. 感谢zzh的讲解. 首先优化基于传统DP,假设树不发生变化,我们就可以利用DP求出带权重心. 考虑修改,我们思路不变,还是从root开始找,但发现这样会被卡成$n^2$,原因是每次经过点太多,为了优化,考虑点分树,由于点分树的性质使得假设我们可以在点分树上找到最优解,那么每次最多经过$log$个节点,可以保证时间复杂度. 然后考虑在点分树转移,假设当前节点为x,我们枚举其在原树中的边,假设当前枚举边的另一端为y,那么由DP可以得出