【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态点分治
題目描述
?傲嬌少女幽香正在玩一個非常有趣的戰略類游戲,本來這個游戲的地圖其實還不算太大,幽香還能管得過來,但是不知道為什么現在的網游廠商把游戲的地圖越做越大,以至于幽香一眼根本看不過來,更別說和別人打仗了。?在打仗之前,幽香現在面臨一個非常基本的管理問題需要解決。?整個地圖是一個樹結構,一共有n塊空地,這些空地被n-1條帶權邊連接起來,使得每兩個點之間有一條唯一的路徑將它們連接起來。在游戲中,幽香可能在空地上增加或者減少一些軍隊。同時,幽香可以在一個空地上放置一個補給站。?如果補給站在點u上,并且空地v上有dv個單位的軍隊,那么幽香每天就要花費dv×dist(u,v)的金錢來補給這些軍隊。由于幽香需要補給所有的軍隊,因此幽香總共就要花費為Sigma(Dv*dist(u,v),其中1<=V<=N)的代價。其中dist(u,v)表示u個v在樹上的距離(唯一路徑的權和)。?因為游戲的規定,幽香只能選擇一個空地作為補給站。在游戲的過程中,幽香可能會在某些空地上制造一些軍隊,也可能會減少某些空地上的軍隊,進行了這樣的操作以后,出于經濟上的考慮,幽香往往可以移動他的補給站從而省一些錢。但是由于這個游戲的地圖是在太大了,幽香無法輕易的進行最優的安排,你能幫幫她嗎??你可以假定一開始所有空地上都沒有軍隊。
輸入
第一行兩個數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輸出
對于幽香的每個操作,輸出操作完成以后,每天的最小花費,也即如果幽香選擇最優的補給點進行補給時的花費。?
樣例輸入
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
樣例輸出
0
1
4
5
6
題解
動態點分治
考慮到帶權重心一定在當前點到距離與權重總和更小的方向上(沒有則當前點為重心),并且這個方向是唯一的,因此可以每次修改都這樣移動,把重心找出。
然而直接移動就是直接暴力,需要更優雅的做法。
考慮在子樹中移動,可以使用動態點分治的點分樹,這樣樹高只有$\log n$,就可以在每層之間移動重心。
那么只需要想辦法求出所有帶權點到某個點的距離,并支持修改即可。
由于一個子樹以外的點到子樹中某點距離可以看作先到根,再從根到該點。所以可以考慮容斥的方法,即用子樹中所有點到該點的距離&個數減去子節點的子樹中所有點到該點的距離&個數。
然后維護這兩個數組即可。由于保證了每個點的度數不超過20,因此每次暴力移動重心,直到所有子節點都比它大為止,此時當前點就是重心。
時間復雜度$O(20n\log^2n)$
#include <cstdio>
#include <algorithm>
#define N 100010
using namespace std;
typedef long long ll;
int head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , log[N << 1] , pos[N] , tot;
int si[N] , mx[N] , sum , root , vis[N] , fa[N] , val[N << 1] , rt , num[N];
ll deep[N] , md[20][N << 1] , va[N] , vb[N];
void add(int x , int y , int z)
{to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x , int fa)
{int i;pos[x] = ++tot , md[0][tot] = deep[x];for(i = head[x] ; i ; i = next[i])if(to[i] != fa)deep[to[i]] = deep[x] + len[i] , dfs(to[i] , x) , md[0][++tot] = deep[x];
}
ll dis(int x , int y)
{ll t = deep[x] + deep[y];x = pos[x] , y = pos[y];if(x > y) swap(x , y);int k = log[y - x + 1];return t - 2 * min(md[k][x] , md[k][y - (1 << k) + 1]);
}
void getroot(int x , int fa)
{int i;si[x] = 1 , mx[x] = 0;for(i = head[x] ; i ; i = next[i])if(!vis[to[i]] && to[i] != fa)getroot(to[i] , x) , si[x] += si[to[i]] , mx[x] = max(mx[x] , si[to[i]]);mx[x] = max(mx[x] , sum - si[x]);if(mx[x] < mx[root]) root = x;
}
void solve(int x)
{int i;vis[x] = 1;for(i = head[x] ; i ; i = next[i])if(!vis[to[i]])sum = si[to[i]] , root = 0 , getroot(to[i] , 0) , fa[root] = x , val[i] = root , solve(root);
}
ll calc(int x)
{int i;ll ans = 0;for(i = x ; i ; i = fa[i]) ans += va[i] + num[i] * dis(x , i);for(i = x ; fa[i] ; i = fa[i]) ans -= vb[i] + num[i] * dis(x , fa[i]);return ans;
}
ll query()
{int x = rt , i , y;ll mn , t;while(1){mn = calc(x) , y = x;for(i = head[x] ; i ; i = next[i])if(val[i] && (t = calc(to[i])) < mn)mn = t , y = val[i];if(x == y) break;x = y;}return mn;
}
int main()
{int n , m , i , j , x , y , z;scanf("%d%d" , &n , &m);for(i = 1 ; i < n ; i ++ ) scanf("%d%d%d" , &x , &y , &z) , add(x , y , z) , add(y , x , z);dfs(1 , 0);for(i = 2 ; i <= tot ; i ++ ) log[i] = log[i >> 1] + 1;for(i = 1 ; (1 << i) <= tot ; i ++ )for(j = 1 ; j <= tot - (1 << i) + 1 ; j ++ )md[i][j] = min(md[i - 1][j] , md[i - 1][j + (1 << (i - 1))]);mx[0] = 1 << 30 , sum = n , getroot(1 , 0) , rt = root , solve(root);while(m -- ){scanf("%d%d" , &x , &y);for(i = x ; i ; i = fa[i]) va[i] += y * dis(x , i) , num[i] += y;for(i = x ; fa[i] ; i = fa[i]) vb[i] += y * dis(x , fa[i]);printf("%lld\n" , query());}return 0;
}
?
?
轉載于:https://www.cnblogs.com/GXZlegend/p/7465051.html
總結
以上是生活随笔為你收集整理的【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态点分治的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信网名霸气超拽女生
- 下一篇: linux驱动之i2c子系统mpu605