CF757G Can Bash Save the Day?(可持久化点分树)

题目
这个题在CF上跑了15min才跑出来、、

真就可持久化呗。
考虑求一个点到一个点集的距离和。
可以用动态点分治也就是点分树来维护点集加删点和询问操作。
对于这个题,差分一下变成:
多次询问前 1... r 1...r 1...r个组成的点集到一个点的距离。
那么就可以可持久化点分树维护 1... r 1...r 1...r的每个版本的点分树。
对于修改操作,发现其实就是对于 x x x这个位置的单点修改,后面的版本都不会变。
维护可持久化点分树的过程就是相当于维护一个多叉的可持久化线段树。。。可以用dfs序判断往哪个儿子走。
注意到可持久化的过程是需要复制儿子链接的。
所以就把树三叉化即可。
常数 × 2 \times 2 ×2
但是这个做法仍然是这个题最不卡空间的做法,用脚开空间都能过。
A C   C o d e \rm AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 400005
#define maxp maxn * 20
#define LL long long
#define lim 20
#define ADJ(i,u) for(int i=info[u],v;i;i=Prev[i])
using namespace std;
 
int n,q,tot,a[maxn],rt[maxn];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cst[maxn<<1],cnt_e;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=c; }
vector<pair<int,int> >G[maxn];
 
void dfs0(int u,int ff){
	int p=u;
	for(int i=0,v;i<G[u].size();i++) if((v=G[u][i].first)^ff){
		if(G[u].size() > 3 && i>=1){
			Node(p,++tot,0),Node(tot,p,0);
			Node(tot,v,G[u][i].second),Node(v,tot,G[u][i].second);
			p = tot;
		}
		else Node(u,v,G[u][i].second),Node(v,u,G[u][i].second);
		dfs0(v,u);
	}
}
 
LL dis[lim][maxn],sm[maxp],zsm[maxp],zcnt[maxp],cnt[maxp],cnt_p;
int vis[maxn],id[maxp],st[maxn],ed[maxn],sz[maxn],dep[maxn];
int E[maxp][3],cte[maxp];
 
void dfs1(int u,int ff,int tsz,int &mn,int &rt){
	int mx = 0;sz[u] = 1;
	ADJ(i,u) if((v=to[i])^ff && !vis[v]){
		dfs1(v,u,tsz,mn,rt);
		sz[u]+=sz[v];
		mx=max(mx,sz[v]);
	} 
	if((mx=max(mx,tsz-sz[u])) < mn)
		mn = mx , rt = u;
}
 
int gert(int u,int tsz){
	int mn = 0x3f3f3f3f  , rt= -1;
	dfs1(u,0,tsz,mn,rt);
	return rt;
}
 
void ser(int u,int ff,LL d,int dp){
	sz[u] =1;
	dis[dp][u] = d;
//	printf("@%d %d %d\n",dp,u,d);
	ADJ(i,u) if((v=to[i])^ff && !vis[v]){
		ser(v,u,d+cst[i],dp);
		sz[u] += sz[v];
	}
}
 
void Build(int u){
	vis[u] = 1;
//printf("@%d\n",u);
	ser(u,0,0,dep[u]);
//	puts("##");
	ADJ(i,u) if(!vis[v=to[i]]){
		int t= gert(v,sz[v]);
		dep[t] = dep[u] + 1;
		E[u][cte[u]++] = t;
		Build(t);
	}
}
 
void dfs2(int u){ 
	static int tot = 0;
	st[u] = ++tot;id[u] = u;
	for(int i=0,v;i<cte[u];i++){
		v=E[u][i];
		dfs2(v);
	}
	ed[u] = tot;
}
 
void ins(int &u,int p){
	id[++cnt_p] = id[u] , sm[cnt_p] = sm[u] , cnt[cnt_p] = cnt[u] , zsm[cnt_p] = zsm[u] , zcnt[cnt_p] = zcnt[u];
	memcpy(E[cnt_p],E[u],sizeof E[u]),cte[cnt_p]=cte[u];
	int nu = id[u];u = cnt_p;
//	printf("@%d %d %lld\n",dep[nu],p,dis[dep[nu]][p]);
	sm[u] += dis[dep[nu]][p];
	cnt[u] ++;
	for(int i=0,v;i<cte[u];i++) if(st[id[v=E[u][i]]] <= st[p] && st[p] <= ed[id[v]]){
		ins(E[u][i],p);v = E[u][i];
		zsm[v] += dis[dep[nu]][p];
		zcnt[v] ++;
	}
}
 
LL qry(int u,int p){
	int nu = id[u] ;
	LL r = sm[u] + cnt[u] * dis[dep[nu]][p];
	//printf("@%d %d %lld %d %lld\n",u,p,r,nu,dis[dep[nu]][p]);
	for(int i=0,v;i<cte[u];i++) if(st[id[v=E[u][i]]] <= st[p] && st[p] <= ed[id[v]])
		r += qry(v,p) - (zsm[v] + zcnt[v] * dis[dep[nu]][p]);
	return r;
}
 
int main(){
	scanf("%d%d",&n,&q);tot = n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<n;i++){
		int u,v,c;
		scanf("%d%d%d",&u,&v,&c);
		G[u].push_back(make_pair(v,c));
		G[v].push_back(make_pair(u,c));
	}
	dfs0(1,0);
	rt[0]  =gert(1,tot);
	Build(rt[0]);
	cnt_p = tot;
	dfs2(rt[0]);
	for(int i=1;i<=n;i++){
		rt[i] = rt[i-1];
		ins(rt[i],a[i]);
	}
	LL ans = 0;
	for(int t,l,r,v;q--;){
		scanf("%d%d",&t,&l);
		ans %= (1<<30);
		l ^= ans;
		if(t == 1){
			scanf("%d%d",&r,&v);
			r ^= ans , v ^= ans;
		//	printf("##");
			ans = qry(rt[r] , v) - qry(rt[l-1] , v);
			printf("%lld\n",ans);
		}
		else{
			swap(a[l],a[l+1]);
			rt[l] = rt[l-1];
			ins(rt[l],a[l]);
		}
	}
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值