【XSY2528】道路建设 LCT 可持久化线段树

题目描述

  给你一个\(n\)个点\(m\)条边图,\(q\)个询问,每次问你边权在\([l,r]\)之间的边组成的最小生成树(森林)的边权和。强制在线。

  \(n,m,q\leq 100000\)

题解

  考虑离线做法。从大到小加边,用LCT维护当前的最小生成树。维护一棵线段树,第\(i\)个位置表示当前的最小生成树中边权为\(i\)的边的权值和。当一条边被加入时就在对应位置加上边权,删掉时就减掉边权。假设已经处理了边权\(\geq i\)的所有边,那么对于所有\(l=i\)的询问的答案就是线段树中\([1,r]\)的数和(等价于\([l,r]\),因为\([1,l-1]\)都是空的)。

  把这棵线段树变成可持久化线段树就可以在线处理询问了。

  我也不知道为什么边权范围是\([1,10000]\)

  时间复杂度:\(O((m+q)\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
namespace lct
{
	int f[200010];
	int a[200010][2];
	pii s[200010];
	pii v[200010];
	int rev[200010];
	void reverse(int x)
	{
		rev[x]^=1;
		swap(a[x][0],a[x][1]);
	}
	void push(int x)
	{
		if(rev[x])
		{
			if(a[x][0])
				reverse(a[x][0]);
			if(a[x][1])
				reverse(a[x][1]);
			rev[x]=0;
		}
	}
	void mt(int x)
	{
		s[x]=v[x];
		if(a[x][0])
			s[x]=max(s[x],s[a[x][0]]);
		if(a[x][1])
			s[x]=max(s[x],s[a[x][1]]);
	}
	int root(int x)
	{
		return !f[x]||(a[f[x]][0]!=x&&a[f[x]][1]!=x);
	}
	void rotate(int x)
	{
		if(root(x))
			return;
		int p=f[x];
		int q=f[p];
		int ps=(x==a[p][1]);
		int qs=(p==a[q][1]);
		int ch=a[x][ps^1];
		if(!root(p))
			a[q][qs]=x;
		a[x][ps^1]=p;
		a[p][ps]=ch;
		if(ch)
			f[ch]=p;
		f[p]=x;
		f[x]=q;
		mt(p);
		mt(x);
	}
	void pushdown(int x)
	{
		if(!root(x))
			pushdown(f[x]);
		push(x);
	}
	void splay(int x)
	{
		pushdown(x);
		while(!root(x))
		{
			int p=f[x];
			if(!root(p))
			{
				int q=f[p];
				if((x==a[p][1])^(p==a[q][1]))
					rotate(x);
				else
					rotate(p);
			}
			rotate(x);
		}
	}
	void access(int x)
	{
		int y=x,t=0;
		while(x)
		{
			splay(x);
			a[x][1]=t;
			mt(x);
			t=x;
			x=f[x];
		}
		splay(y);
	}
	void change(int x)
	{
		access(x);
		reverse(x);
	}
	void link(int x,int y)
	{
		change(x);
		f[x]=y;
	}
	void cut(int x,int y)
	{
		change(x);
		access(y);
		f[a[y][0]]=0;
		a[y][0]=0;
	}
	pii query(int x,int y)
	{
		change(x);
		access(y);
		return s[y];
	}
	int findroot(int x)
	{
		access(x);
		while(a[x][0])
			x=a[x][0];
		splay(x);
		return x;
	}
}
namespace seg
{
	int n=0;
	int ls[5000010];
	int rs[5000010];
	int s[5000010];
	int rt[100010];
	int change(int p1,int x,int v,int l,int r)
	{
		int p=++n;
		ls[p]=ls[p1];
		rs[p]=rs[p1];
		s[p]=s[p1]+v;
		if(l==r)
			return p;
		int mid=(l+r)>>1;
		if(x<=mid)
			ls[p]=change(ls[p1],x,v,l,mid);
		if(x>mid)
			rs[p]=change(rs[p1],x,v,mid+1,r);
		return p;
	}
	int query(int p,int l,int r,int L,int R)
	{
		if(l<=L&&r>=R)
			return s[p];
		int mid=(L+R)>>1;
		int res=0;
		if(l<=mid)
			res+=query(ls[p],l,r,L,mid);
		if(r>mid)
			res+=query(rs[p],l,r,mid+1,R);
		return res;
	}
}
struct edge
{
	int x,y,d;
};
int cmp(edge a,edge b)
{
	return a.d<b.d;
}
edge a[100010];
int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	int n,m,on;
	scanf("%d%d%d",&n,&m,&on);
	int i;
	for(i=1;i<=m;i++)
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].d);
	sort(a+1,a+m+1,cmp);
	int j=m;
	seg::rt[n+1]=0;
	for(i=1;i<=n;i++)
		lct::v[i]=pii(0,0);
	for(i=10000;i>=1;i--)
	{
		seg::rt[i]=seg::rt[i+1];
		while(j>=1&&a[j].d==i)
		{
			int rx=lct::findroot(a[j].x);
			int ry=lct::findroot(a[j].y);
			if(rx==ry)
			{
				pii s=lct::query(a[j].x,a[j].y);
				if(s.first>a[j].d)
				{
					lct::cut(a[s.second].x,s.second+n);
					lct::cut(a[s.second].y,s.second+n);
					lct::v[j+n]=pii(a[j].d,j);
					lct::link(a[j].x,j+n);
					lct::link(a[j].y,j+n);
					seg::rt[i]=seg::change(seg::rt[i],s.first,-s.first,1,10000);
					seg::rt[i]=seg::change(seg::rt[i],a[j].d,a[j].d,1,10000);
				}
			}
			else
			{
				lct::v[j+n]=pii(a[j].d,j);
				lct::link(a[j].x,j+n);
				lct::link(a[j].y,j+n);
				seg::rt[i]=seg::change(seg::rt[i],a[j].d,a[j].d,1,10000);
			}
			j--;
		}
	}
	int q;
	scanf("%d",&q);
	int l,h,last=0;
	for(i=1;i<=q;i++)
	{
		scanf("%d%d",&l,&h);
		l-=on*last;
		h-=on*last;
		last=seg::query(seg::rt[l],1,h,1,10000);
		printf("%d\n",last);
	}
	return 0;
}
posted @ 2018-03-06 10:56  ywwyww  阅读(233)  评论(0编辑  收藏  举报