cf757G. Can Bash Save the Day?

簡述題意:給一棵n個節點的數和一個n的數組(n的排列),支持兩個操作:1 詢問a[l]~a[r]的所有點到某個點的距離,2 交換a[x]與a[x+1]。

一開始想到了這道題bzoj4012: [HNOI2015]開店,大概看了之後發現是開店的加強版。我想到像開店一樣使用主席樹處理,顯然操作2很容易。然而

他的時間是一個log。大概看了題解,感覺套路~   畢竟開店的題解大多是log^2。。。


//以下來自翻譯

對於詢問l~r,可以拆成1~l-1,1~r的詢問。想要詢問一個點到一些點的距離和,可以在重心樹(Centroid Tree)上直接跑。然後給出一個可持久化重心樹(Persistent Centroid Tree),用與線段樹類似的思路,維護當前區間的所有點數量 與到重心父親的距離和,每加入一個節點只需要修改它在重心樹上到root的距離。由於重心樹深度是O(logn),所以直接可持久化每次是O(Σ重心樹上兒子數),這顯然是不優的。

然後就有了O(n)把一般樹轉換爲O(n)節點的二叉樹的算法,就是開店中的限制。之後就是簡單的實現了


有一些細節:儘管轉化爲二叉樹,重心樹節點仍可以有3個兒子。可能需要RMQ O(1)求兩點間距離。我的空間炸炸炸。這道題共151個測試點,時限5000ms。


這樣就得到了一個時間O(nlogn),空間O(nlogn)的優秀算法。

由於窩比較弱,如果有分析錯誤請指出。。。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 800005
#define M 15000005
#define ll long long
using namespace std;

int n,q,p,x,y,z,RT,w[N];
int to[N],len[N],nxt[N],fst[N],u[N],l=1;
int To[N],Len[N],Nxt[N],Fst[N],L;
int a[N],b[N],c[N][3],cnt,st[N],ed[N],Cnt;
int Mx[N],sz[N],fa[N];
int ly,e[N],lg[N],pos[N];
int rt[N],s[M][3],used[M],nd;

ll r[N][26],dep[N],f[M],g[M],ans;

void link(int x,int y,int z)
{
	to[++l]=y;len[l]=z;nxt[l]=fst[x];fst[x]=l;
	to[++l]=x;len[l]=z;nxt[l]=fst[y];fst[y]=l;
}
void Link(int x,int y,int z)
{
	To[++L]=y;Len[L]=z;Nxt[L]=Fst[x];Fst[x]=L;
	To[++L]=x;Len[L]=z;Nxt[L]=Fst[y];Fst[y]=L;
}
void edg(int x,int l,int r)
{
	if (l==r)
	{
		link(x,a[l],b[l]);
		return;
	}
	int mid=l+r>>1,tmp=++cnt;
	
	edg(tmp,l,mid);
	edg(tmp,mid+1,r);
	link(x,tmp,0);
}
void dfs(int x,int f)
{
	int t=0;
	for (int i=Fst[x];i;i=Nxt[i])
		if (To[i]!=f)
			a[++t]=To[i],b[t]=Len[i];
	if (t)
	{
		int mid=t+1>>1; 
		edg(x,1,mid);
		if (t!=1) edg(x,mid+1,t);
	}
	for (int i=Fst[x];i;i=Nxt[i])
		if (To[i]!=f)
			dfs(To[i],x);
}
void Dfs(int x,int f)
{
	sz[x]=1;Mx[x]=0;
	for (int i=fst[x];i;i=nxt[i])
	{
		if (!u[i]&&to[i]!=f)
		{
			Dfs(to[i],x);
			sz[x]+=sz[to[i]];
			Mx[x]=max(Mx[x],sz[to[i]]);
		}
	}
}
void Get(int x,int f,int size,int &G)
{
	if (max(Mx[G],size-sz[G])>max(Mx[x],size-sz[x])) G=x;
	for (int i=fst[x];i;i=nxt[i])
		if (!u[i]&&to[i]!=f) Get(to[i],x,size,G);

}
int build(int x)
{
	int G=x,sum=0;
	Dfs(x,0);
	if (sz[x]==1) return x;
	Get(x,0,sz[x],G);
	for (int i=fst[G];i;i=nxt[i])
		if (!u[i])
		{
			u[i]=u[i^1]=1;
			int t=build(to[i]);
			c[G][sum++]=t;
			fa[t]=G;
		}
	return G;
}
void DFS(int x,int f)
{
	r[pos[x]=++ly][0]=dep[x];
	for (int i=fst[x];i;i=nxt[i])
		if (to[i]!=f)
		{
			dep[to[i]]=dep[x]+len[i];
			DFS(to[i],x);
			r[++ly][0]=dep[x];
		}
}
void DFS(int x)
{
	st[x]=++Cnt;
	for (int i=0;i<3;i++)
		if (c[x][i])
			DFS(c[x][i]);
	ed[x]=Cnt;
}
ll dis(int x,int y)
{
	if (!x||!y) return 0;
	x=pos[x];y=pos[y];
	if (x>y) swap(x,y);
	int t=lg[y-x+1];
	return r[x][0]+r[y][0]-2*min(r[x][t],r[y-e[t]+1][t]);
}

void mdy(int x,int &y,int p,int q)
{
	if (!y) y=++nd;
	
	g[y]=g[x]+1;
	f[y]=f[x]+dis(fa[p],q);
	used[y]=used[x];
	if (p==q) used[y]=1;
	for (int i=0;i<3;i++)
		if (c[p][i]&&st[q]>=st[c[p][i]]&&st[q]<=ed[c[p][i]])
			mdy(s[x][i],s[y][i]=0,c[p][i],q);
		else s[y][i]=s[x][i];	
}

ll qry(int x,int y,int z)
{
	if (!x) return 0;
	if (z==y)
	{
		ll ans=0;
		for (int i=0;i<3;i++)
			if (c[y][i]) ans+=f[s[x][i]];
		return ans;
	}
	ll ans=used[x]*dis(y,z);
	for (int i=0;i<3;i++)
		if (c[y][i]&&st[z]>=st[c[y][i]]&&st[z]<=ed[c[y][i]])
			ans+=qry(s[x][i],c[y][i],z);
		else ans+=f[s[x][i]]+g[s[x][i]]*dis(y,z);
	return ans;
}

int main()
{
	scanf("%d%d",&n,&q);
	for (int i=1;i<=n;i++)
		scanf("%d",&w[i]);
	for (int i=2;i<=n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		Link(x,y,z);
	}
	cnt=n;
	dfs(1,0);//cnt=2n
	L=0;
	memset(Fst,0,sizeof Fst);
	
	RT=build(1);
	DFS(RT);
	DFS(1,0);//ly=4n
	
	lg[1]=0;
	for (int i=2;i<=ly;i++)
		lg[i]=lg[i>>1]+1;
	e[0]=1;
	for (int i=1;i<=lg[ly];i++)
		e[i]=e[i-1]<<1;
	for (int i=1;i<=lg[ly];i++)
	{
		for (int j=1;j<=ly;j++)
			r[j][i]=min(r[j][i-1],r[j+e[i-1]][i-1]);
	}
	
	for (int i=1,apa=0;i<=n;i++)
	{
		mdy(rt[i-1],rt[i],RT,w[i]);//nd+=log 2n
	}
	while(q--)
	{
		scanf("%d",&p);
		if (p==1)
		{
			scanf("%d%d%d",&x,&y,&z);
			x^=ans%(1<<30);
			y^=ans%(1<<30);
			z^=ans%(1<<30);
			printf("%lld\n",ans=qry(rt[y],RT,z)-qry(rt[x-1],RT,z));
		} 
		else
		{
			scanf("%d",&x);
			x^=ans%(1<<30);
			mdy(rt[x-1],rt[x],RT,w[x+1]);
			swap(w[x],w[x+1]);
		}
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章