【可持久化点分树??】CF757G Can Bash Save the Day?

【题目】
CF
一棵 n n n个点的树和一个排列 p i p_i pi,边有边权,支持两种操作:

  • l   r   x l\ r\ x l r x,询问 ∑ i = l r d i s ( p i , x ) \sum\limits _{i=l}^{r} dis(p_i,x) i=lrdis(pi,x)
  • x x x,交换 p x , p x + 1 p_x,p_{x+1} px,px+1

n , q ≤ 2 × 1 0 5 n,q\leq 2\times 10^5 n,q2×105,强制在线

【解题思路】
首先考虑没有修改怎么做。

这就是一个经典的动态点分治题(HNOI2015开店),我们将每个分治中心内存的信息按排列排序,那么每次询问就是一个前缀的差分。

现在有修改,观察到修改会影响到点分树一条链上的信息,我们可以 YY \text{YY} YY一个可持久化点分树出来:即对于每一个前缀维护一棵点分树。

于是对于第二种操作,我们只需要修改第 x x x棵点分树的信息即可。

但是点分树的可持久化有一个很不好的性质,我们可持久一个点的时候,需要保存这个点所有儿子的信息,显然这个可能是 O ( n ) O(n) O(n)级别的。怎么办?重构呗!我们直接按边分治重构树的方法,将原树重构成一棵二叉树即可。

这里注意的是点分树仍有可能有三个儿子,于是我们可能需要 O ( 1 ) LCA O(1)\text{LCA} O(1)LCA之类的东西了。

感觉不太可能把点分树重构了吧?

这样时空复杂度是 O ( n log ⁡ n ) O(n\log n) O(nlogn)的。

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
using namespace std;

typedef long long ll;
const int N=4e5+10,msk=(1<<30)-1;
int n,m,cnt;

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;

namespace Graph
{
	int dtsz,ind,sum,root;
	int mx[N],siz[N];
	int du[N],dp[N],vis[N],pos[N],rt[N],Log[N<<1],fc[22];
	ll mi[20][N<<1],dis[N];
	vector<int>st[N];
	struct Tway
	{
		int v,w,nex;
		Tway(int _v=0,int _w=0,int _nex=0):v(_v),w(_w),nex(_nex){}	
	};
	struct Tree
	{
		Tway e[N<<1];int tot,head[N];
		void add(int u,int v,int w)
		{
			e[++tot]=Tway(v,w,head[u]);head[u]=tot;
			e[++tot]=Tway(u,w,head[v]);head[v]=tot;
		}
	}T1,T2;
	struct DT
	{
		ll s1,s2;
		int ch[3],sz,id;
	}tr[N*30];

	void dfs1(int x,int f)
	{
		int tmp=0,las=0;
		for(int i=T1.head[x];i;i=T1.e[i].nex)
		{
			int v=T1.e[i].v,w=T1.e[i].w;
			if(v==f) continue; ++tmp;
			if(tmp==1) T2.add(x,v,w),las=x;
			else if(tmp==du[x]-(x!=1)) T2.add(las,v,w);
			else ++cnt,T2.add(las,cnt,0),T2.add(cnt,v,w),las=cnt;
		}
		for(int i=T1.head[x];i;i=T1.e[i].nex) 
		{
			int v=T1.e[i].v;
			if(v!=f) dfs1(v,x);
		}
	}
	void dfs2(int x,int f)
	{
		pos[x]=++ind;mi[0][ind]=dis[x];
		for(int i=T2.head[x];i;i=T2.e[i].nex)
		{
			int v=T2.e[i].v,w=T2.e[i].w;
			if(v==f) continue;
			dis[v]=dis[x]+w;dfs2(v,x);mi[0][++ind]=dis[x];
		}
	}
	ll lca(int x,int y)
	{
		x=pos[x];y=pos[y];
		if(x>y) swap(x,y);
		int t=Log[y-x+1];
		return min(mi[t][x],mi[t][y-fc[t]+1]);
	}
	ll getdis(int x,int y){return dis[x]+dis[y]-lca(x,y)*2;}
	void initlca()
	{
		for(int i=2;i<N*2;++i) Log[i]=Log[i>>1]+1;
		fc[0]=1;for(int i=1;i<20;++i) fc[i]=fc[i-1]<<1;
		for(int j=1;j<20;++j) for(int i=1;i+fc[j]-1<=ind;++i)
			mi[j][i]=min(mi[j-1][i],mi[j-1][i+fc[j-1]]);
	}
	void getroot(int x,int f)
	{
		siz[x]=1;mx[x]=0;
		for(int i=T2.head[x];i;i=T2.e[i].nex)
		{
			int v=T2.e[i].v;
			if(vis[v] || v==f) continue;
			getroot(v,x);siz[x]+=siz[v];mx[x]=max(mx[x],siz[v]);
		}
		mx[x]=max(mx[x],sum-siz[x]);
		if(mx[x]<mx[root]) root=x;
	}
	void solve(int x)
	{
		vis[x]=1;tr[x].id=x;
		for(int i=T2.head[x];i;i=T2.e[i].nex)
		{
			int v=T2.e[i].v;
			if(vis[v]) continue;
			sum=siz[v];root=0;getroot(v,x);dp[root]=dp[x]+1;
			for(int j=0;j<dp[x];++j) st[root].pb(st[x][j]);
			for(int j=0;j<3;++j) if(!tr[x].ch[j])
			{
				tr[x].ch[j]=root;st[root].pb(j);
				break;
			}
			solve(root);
		}
	}
	void insert(int x,int &y,int a,int b,int f)
	{
		y=++dtsz;int z=tr[x].id;
		//printf("%d %d %d %d %d %d\n",x,y,z,a,b,f);
		memcpy(tr[y].ch,tr[x].ch,sizeof(tr[x].ch));tr[y].id=z;
		tr[y].sz=tr[x].sz+b;tr[y].s1=tr[x].s1+getdis(z,a)*b;tr[y].s2=tr[x].s2;
		if(f) tr[y].s2+=getdis(tr[f].id,a)*b;
		if(tr[y].id==a) return;
		insert(tr[x].ch[st[a][dp[z]]],tr[y].ch[st[a][dp[z]]],a,b,y);
	}
	ll query(int x,int y)
	{
		ll res=0;
		for(int i=0;i<dp[y];++i)
		{
			int u=tr[x].ch[st[y][i]];
			res+=(tr[x].sz-tr[u].sz)*getdis(tr[x].id,y)+(tr[x].s1-tr[u].s2);
			x=u;
		}
		return res+tr[x].s1;
	}
}
using namespace Graph;

namespace DreamLolita
{
	int p[N];
	ll ans;
	void solve()
	{
		cnt=n=read();m=read();
		for(int i=1;i<=n;++i) p[i]=read();
		for(int i=1;i<n;++i) 
		{
			int u=read(),v=read(),w=read();
			++du[u];++du[v];T1.add(u,v,w);
		}
		dfs1(1,0);dfs2(1,0);initlca();
		mx[0]=sum=cnt;getroot(1,0);rt[0]=root;Graph::solve(root);
		dtsz=cnt;for(int i=1;i<=n;++i) insert(rt[i-1],rt[i],p[i],1,0);

		while(m--)
		{
			if(read()&1)
			{
				int l=read()^ans,r=read()^ans,v=read()^ans;
				ans=query(rt[r],v)-query(rt[l-1],v);
				writeln(ans);ans&=msk;
			}
			else
			{
				int x=read()^ans;
				insert(rt[x-1],rt[x],p[x],0,0);swap(p[x],p[x+1]);insert(rt[x],rt[x],p[x],1,0);
			}
		}
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("CF757G.in","r",stdin);
	freopen("CF757G.out","w",stdout);
#endif
	DreamLolita::solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值