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

时间:2023-03-09 03:44:36
BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)

这种动态点分治嘛,GDKOI时听打到了,也有同学讲到了,所以印象比较深刻也就想出来了,然后就在实现方面卡了好久= =

不得不说CLJ说得真的太简单了,实现方面根本没提。

首先我们可以先用树分治构建出这棵树的分治树,也就是把这棵树的重心作为根节点然后子树为他的子树的重心这样递归下去,然后每个节点存的是其子树的信息。

对于每个节点我们保存这个子树的dv的总和已经把该节点作为点的答案值

这样对于修改能在log n的时间内解决

寻找答案的时候,我们可以发现,如果现在节点的子树dv和*2大于总节点,那么向那个方向过去一定比原方案好

我们先从根节点开始,若发现答案在某棵子树时,我们考虑如何使其儿子节点的答案转变为整个树的答案,可以发现把除这个子树外的所有节点可以缩成一个节点并连在这棵子树上,然后就可以一直这样做下去,找到操作之后再把这些撤销

因此还是得维护一些奇奇怪怪的东西,但打出来还是挺短的

Code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define maxn 100100
typedef long long ll;
typedef pair<ll,ll> ii;
typedef pair<ll,ii> iii;
#define fi first
#define se second
#define pb push_back
struct edges{int to,next,dist;}edge[maxn*];
int l,next[maxn];
inline void addedge(int x,int y,int z) {
edge[++l]=(edges){y,next[x],z};next[x]=l;
edge[++l]=(edges){x,next[y],z};next[y]=l;
}
bool done[maxn];
int s[maxn],f[maxn],root,size;
void dfs(int u,int fa){
s[u]=;f[u]=;
int v=;
for (int i=next[u];i;i=edge[i].next) {
if (done[v=edge[i].to]||v==fa) continue;
dfs(v,u);
s[u]+=s[v];
f[u]=max(f[u],s[v]);
}
f[u]=max(f[u],size-s[u]);
if (f[u]<f[root]) root=u;
}
vector<ii> pre[maxn];
void getdist(int u,int fa,int tar,int dist) {
pre[u].pb(ii(tar,dist));
s[u]=;int v=;
for (int i=next[u];i;i=edge[i].next) {
if (done[v=edge[i].to]||v==fa) continue;
getdist(v,u,tar,dist+edge[i].dist);
s[u]+=s[v];
}
}
vector<iii> ch[maxn];
void work(int u){
done[u]=;
int v=;
pre[u].pb(ii(u,));
for (int i=next[u];i;i=edge[i].next) {
if (done[v=edge[i].to]) continue;
getdist(v,,u,edge[i].dist);
f[]=size=s[v];
dfs(v,root=);
ch[u].pb(iii(root,ii(v,edge[i].dist)));
work(root);
}
}
ll cnt[maxn],sum[maxn];
vector<ll> sumdist[maxn];
inline void update(int x,ll y,ll z){
for (int i=;i<pre[x].size();i++) {
int u=pre[x][i].fi;
cnt[u]+=y;sum[u]+=z+y*pre[x][i].se;
if (i!=pre[x].size()-) {
int j=;
for (;j<ch[u].size();j++)
if (ch[u][j].fi==pre[x][i+].fi) sumdist[u][j]+=z+y*pre[x][i].se;
}
}
}
int realroot;
vector<iii> record;
inline ll query(){
int x=realroot;
int mx=;
record.clear();
while (x){
mx=;
for (int i=;i<ch[x].size();i++)
if (cnt[ch[x][mx].fi]<cnt[ch[x][i].fi]) mx=i;
if (ch[x].size()==||cnt[ch[x][mx].fi]*<=cnt[x]) {
ll ans=sum[x];
for (int i=;i<record.size();i++)
update(record[i].fi,record[i].se.fi,record[i].se.se);
return ans;
}
int v=ch[x][mx].fi;
record.pb(iii(ch[x][mx].se.fi,ii(-(cnt[x]-cnt[v]),
-(sum[x]-sumdist[x][mx]+(cnt[x]-cnt[v])*ch[x][mx].se.se))));
update(ch[x][mx].se.fi,cnt[x]-cnt[v],
sum[x]-sumdist[x][mx]+(cnt[x]-cnt[v])*ch[x][mx].se.se);
x=v;
}
}
int main(){
int n,Q;
scanf("%d%d",&n,&Q);
for (int i=;i<n;i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}
f[]=size=n;
dfs(,root=);
realroot=root;
work(root);
for (int i=;i<=n;i++) sumdist[i]=vector<ll>(ch[i].size(),);
while (Q--) {
int x,y;
scanf("%d%d",&x,&y);
update(x,y,);
printf("%lld\n",query());
}
return ;
}