zoukankan      html  css  js  c++  java
  • [ZJOI2015]幻想乡战略游戏 解题报告 (动态点分治)

    [ZJOI2015]幻想乡战略游戏

    题意

    有一棵大小为 (n) 的带权树, 每个点有一个权值, 权值可以修改 (q) 次,

    找出一个补给点 (x) , 使得 (sum_{u in V} val[u] imes dis(x,u)) 最小, 并求出这个最小值.


    一句话 : 求带权重心 (zsy说的)


    附加条件 : 树中所有点的度数不超过 (20).

    思路

    一道你以为复杂度过不了, 但其实是过得了的题.


    首先, 我们假定先选了一个点 (u) 作为补给点, 我们可以算出它的所有子节点 (v) 作为补给点时所需要的代价.

    那么我们选取一个代价最小的点, 带权重心就一定在它的子树内;

    假若点 (u) 的代价最小, 那么点 (u) 就是带权重心.


    所以我们可以得到一个大致思路 :

    先从一个点开始, 每次决定要往哪个子节点走, 然后在这个子节点的子树内递归处理这个子任务.


    那么为了减小递归次数, 我们可以用动态点分治, 沿着点分树一层一层往下处理, 最后经过 (log n) 层找到带权重心.

    而我们在每次层中的操作就是 : 枚举当前点的每一个子节点, 找出最小的那个, 然后跳向它对应的重心.


    那我们现在的问题就是 : 该如何计算一个点作为补给点时所需的代价.

    首先, 有了动态点分治, 我们可以维护一个点作为补给点时, 它在 点分树中的子树 中的点到它的中代价, 设为 (v1),

    那么, 设当前点为 (u) ,根据 (ft[u]) ( (u) 在点分树上的父亲) 的 (v1), 再加上 一些其他东西 , 我们就可以求出 (ft[u]) 在点分树中的子树的点到点 (u) 的总代价, 这样一层一层往上, 那我们就可以得到整棵树到点 (u) 的代价.

    那, 这些 "其他东西" 是什么呢?

    我们可以把 (ft[u]) 的子树分为两部分, (注 : 从这里开始, "子树" 均指 点分树上的子树.)

    1. 在点 (u) 的子树中的部分.
    2. 不在点 (u) 的子树中的部分.

    第一部分所需的代价就是 (v1[u]),

    为了计算第二部分的代价, 我们增添几个值,

    1. (v2[u]) : 表示点 (u) 子树中的点到 (ft[u]) 所需的代价.
    2. (s2[u]) : 表示点 (u) 子树中的点的总权值.

    那么第二部分的代价就为 (v1[ft[u]]-v2[u]+dis(u,ft[u])*(s2[fa]-s2[u])).

    可以这样理解 :

    (v1[ft[u]]-v2[u]) 表示将点 (u) 的子树的代价去除.

    (+dis(u,ft[u])*(s2[fa]-s2[u])) 表示将到 (ft[u]) 的代价转为到 (u) 的代价.


    由上可得, 求一个点的代价的时间复杂度为 : 在点分树上逐层向上的 (log n) 乘上算 (dis)(log n), 总共为 (O(log^2 n)).

    再乘上之前的复杂度, 得到一次询问的复杂度为 (O(20log^3 n)),

    那么总复杂度为 (O(20nlog^3 n)) (因为 (q)(n) 是相同规模的), 大概有 (10^9), 看起来就很不靠谱, 但是如果把用树剖求 (lca), 这个复杂度就跑不满, 再加上 (6s) 的时限, 还是可以过的, 并且洛谷上最优解总时间只有 (500) 多毫秒....

    代码

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int _=1e5+7;
    const int __=2e5+7;
    const int L=20;
    const int inf=0x3f3f3f3f;
    bool be;
    int n,q,dep[_],sz[_],top[_],son[_],fat[_],rt,minx,ft[_],rtt[__];   // v1: to self  v2: to father
    ll v1[_],v2[_],s2[_],dis[_];
    int lst[_],nxt[__],to[__],tot;
    ll len[__];
    bool vis[_];
    bool en;
    void add(int x,int y,ll w){ nxt[++tot]=lst[x]; to[tot]=y; len[tot]=w; lst[x]=tot; }
    void dfs1(int u,int fa){
      fat[u]=fa;
      dep[u]=dep[fa]+1;
      for(int i=lst[u];i;i=nxt[i]){
        int v=to[i];
        if(v==fa) continue;
        dis[v]=dis[u]+len[i];
        dfs1(v,u);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
      }
    }
    void dfs2(int u){
      if(son[u]){
        top[son[u]]=top[u];
        dfs2(son[u]);
      }
      for(int i=lst[u];i;i=nxt[i])
        if(!top[to[i]]){
          top[to[i]]=to[i];
          dfs2(to[i]);
        }
    }
    int Lca(int x,int y){
      while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fat[top[x]];
      }
      return dep[x]<=dep[y] ?x :y;
    }
    ll dist(int x,int y){ return dis[x]+dis[y]-2*dis[Lca(x,y)]; }
    void g_rt(int u,int fa,int sum){
      int maxn=0; sz[u]=1;
      for(int i=lst[u];i;i=nxt[i]){
        int v=to[i];
        if(vis[v]||v==fa) continue;
        g_rt(v,u,sum);
        sz[u]+=sz[v];
        maxn=max(maxn,sz[v]);
      }
      maxn=max(maxn,sum-sz[u]);
      if(maxn<minx){ minx=maxn; rt=u; }
    }
    void init(int u,int lrt,int sum){
      minx=inf;
      g_rt(u,0,sz[u]<sz[lrt] ?sz[u] :sum-sz[lrt]);
      sum=sz[u];
      ft[rt]=lrt; 
      vis[rt]=1; u=rt;
      for(int i=lst[u];i;i=nxt[i])
        if(!vis[to[i]]){
          init(to[i],u,sum);
          rtt[i]=rt;
        }
      rt=u;
      vis[rt]=0;
    }
    void modify(int x,ll w){
      int u=x,fa=ft[u];
      ll len;
      while(fa){
        len=dist(fa,x);
        //printf("u: %d fa: %d len: %d
    ",u,fa,len);
        v1[fa]+=w*len;
        v2[u]+=w*len;
        s2[u]+=w;
        u=ft[u]; fa=ft[u];
      }
      s2[u]+=w;
    }
    ll cst(int x){
      int u=x,fa=ft[u];
      ll len,res=v1[x];
      while(fa){
        len=dist(x,fa);
        res+=v1[fa]-v2[u]+len*(s2[fa]-s2[u]);
        u=ft[u]; fa=ft[u];
      }
      return res;
    }
    ll query(){
      int u=rt;
      ll ans=1e17;
      while(u){
        ans=min(ans,cst(u));
        int x=0;
        ll minx=1e17;
        for(int i=lst[u];i;i=nxt[i]){
          if(!rtt[i]) continue;
          int v=to[i];
          ll tmp=cst(v);
          if(tmp<minx){ minx=tmp; x=rtt[i]; }
        }
        u=x;
      }
      return ans;
    }
    int main(){
      //freopen("x.in","r",stdin);
      //freopen("x.out","w",stdout);
      cin>>n>>q;
      int x,y; ll w;
      for(int i=1;i<n;i++){
        scanf("%d%d%lld",&x,&y,&w);
        add(x,y,w);
        add(y,x,w);
      }
      init(1,0,n);
      dfs1(1,0);
      top[1]=1;
      dfs2(1);
      for(int i=1;i<=q;i++){
        scanf("%d%lld",&x,&w);
        modify(x,w);
        printf("%lld
    ",query());
      }
    }
    
  • 相关阅读:
    JDK安装,环境配置
    XSS攻击测试代码
    css3 input边框蓝光特效
    PDFobject插件使用,PDF在线查看插件
    网页中嵌入可以点击“运行代码”执行html/css/js代码
    SmohanTimeLine.js 酷炫的时间轴效果
    java进行文件上传,带进度条
    javascript读取xml文件
    C# 动态类型与动态编译简介
    Mac 命令行美化
  • 原文地址:https://www.cnblogs.com/BruceW/p/12130377.html
Copyright © 2011-2022 走看看