P3345 [ZJOI2015]幻想乡战略游戏

Posted andyc_03

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3345 [ZJOI2015]幻想乡战略游戏相关的知识,希望对你有一定的参考价值。

【题意】

给定一个树,要求如下操作

每次修改一个点权之后,询问选择树上哪一个点作为补给站u可以使dis(u,i)*vi的和最小

【分析】

直接考虑修改点权会带来什么影响,建好点分树以后

我们对于每个点记录dis1表示子树内对自己的贡献,dis2表示子树内到fa的贡献,sumv表示子树的点权和,每次跳fa来修改sumv、dis1、dis2

在询问的时候,考虑从点分树的根节点开始,先计算自己节点的答案,然后计算各个儿子的答案,如果有更优的便往下走,直到不存在更优的返回答案即可

这样做是基于一种贪心的思想,考虑如果v是u的儿子,v比u更优,这样的儿子最多有一个

也就是说如果现在节点的子树dv和*2大于总节点,那么向那个方向过去一定比原方案好

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,m,head[maxn],tot;
struct edge
{
    int to,nxt,v;
}e[maxn<<1],dfse[maxn<<1];
void add(int x,int y,int z)
{
    e[++tot].to=y; e[tot].nxt=head[x]; e[tot].v=z; head[x]=tot;
}
int h[maxn],cnt;
void dfsadd(int x,int y,int z)
{
    dfse[++cnt].to=y; dfse[cnt].nxt=h[x];dfse[cnt].v=z; h[x]=cnt;
}
int st[maxn<<1][20],dis[maxn],dfn[maxn],dfstimes;
void dfs(int u,int fa)
{
    dfn[u]=++dfstimes;
    st[dfn[u]][0]=dis[u];
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa) continue;
        dis[to]=dis[u]+e[i].v;
        dfs(to,u);
        st[++dfstimes][0]=dis[u];
    }    
}
int lg[maxn<<1],cs;
void lca_init()
{
    lg[0]=-1;
    for(int i=1;i<=n*2;i++) lg[i]=lg[i>>1]+1;
    while((1<<(cs+1))<=n*2) cs++;
    for(int j=1;j<=cs;++j)
        for(int i=1;i+(1<<j)-1<=(n<<1);++i)
            st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
int root,size,gsiz,siz[maxn],vis[maxn];
void findrt(int u,int fa)
{
    siz[u]=1;
    int maxsiz=0;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || vis[to]) continue;
        findrt(to,u);
        maxsiz=max(maxsiz,siz[to]);
        siz[u]+=siz[to];
    }
    maxsiz=max(maxsiz,size-siz[u]);
    if(maxsiz<gsiz)
    {
        gsiz=maxsiz;
        root=u;
    }
}
int fa[maxn];
void solve(int u)
{
    vis[u]=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to]) continue;
        root=0; size=gsiz=siz[to];
        findrt(to,0);
        dfsadd(u,root,to);
        fa[root]=u;
        solve(root);
    }
}
ll sumv[maxn],dis1[maxn],dis2[maxn];
ll lcadis(int x,int y)
{
    if(dfn[x]>dfn[y]) swap(x,y);
    int i=lg[dfn[y]-dfn[x]+1];
    return min(st[dfn[x]][i],st[dfn[y]-(1<<i)+1][i]);
}
ll calcdis(int x,int y)
{
    return dis[x]+dis[y]-2*lcadis(x,y);
}
void update(int x,int y)
{
    sumv[x]+=y;
    for(int i=x;fa[i];i=fa[i])
    {
        ll dist=calcdis(x,fa[i])*y;
        dis1[fa[i]]+=dist;
        dis2[i]+=dist;
        sumv[fa[i]]+=y;
    }
}
ll calc(int u)
{
    ll ans=dis1[u];
    for(int p=u;fa[p];p=fa[p])
    {
        ll dist=calcdis(fa[p],u);
        ans+=dis1[fa[p]]-dis2[p];
        ans+=dist*(sumv[fa[p]]-sumv[p]);
    }
    return ans;
}
ll query(int u)
{
    ll ans=calc(u);
    for(int i=h[u];i;i=dfse[i].nxt)
    {
        ll tmp=calc(dfse[i].v);
        if(tmp<ans) return query(dfse[i].to);
    }
    return ans;
}
int main()
{

    scanf("%d%d",&n,&m);
    int x,y,z;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z); add(y,x,z);
    }
    dfs(1,0);
    lca_init();
    size=gsiz=n;
    findrt(1,0);
    int lsrt=root;
    solve(root);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        update(x,y);
        printf("%lld\\n",query(lsrt));
    }
    return 0;
}

 

以上是关于P3345 [ZJOI2015]幻想乡战略游戏的主要内容,如果未能解决你的问题,请参考以下文章

P3345 [ZJOI2015]幻想乡战略游戏

luogu P3345 [ZJOI2015]幻想乡战略游戏

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

并不对劲的bzoj3924:loj2135:p3345:[ZJOI2015]幻想乡战略游戏

[ZJOI2015]幻想乡战略游戏——动态点分治

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