码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ 2759 一个动态树好题 Link-Cut-Tree+扩展欧几里得

时间:2014-10-25 09:20:13      阅读:284      评论:0      收藏:0      [点我收藏+]

标签:bzoj   bzoj2759   link-cut-tree   扩展欧几里得   数论   

题目大意:给定n个形如xi=ki*x_pi+bi mod p的同余方程组 支持修改操作和求解操作

确实好题 感谢此题作者 顺便吐槽一下作者的Splay不加空节点太蛋疼了0.0

将每个点i的父亲设为pi 我们将会得到一座基环树林 将环上的一条边拆掉,在边的起始节点新开个域special_father记录这条边(P.S:好浪费 但是没办法)

于是我们得到了一座森林 显然可以用LCT来维护 每个节点的权值是个二元组(k,b),记录每个点关于答案的线性关系,合并时左侧代入右侧中

查询时将root的special_father进行Access+Splay操作 然后借助这个环通过EXGCD求出root->special_father的值 然后Access+Splay(x)代入出解

若环上k=1 讨论b 若b=0则无穷多解 否则无解 注意k=0时要加一些处理 正常求EXGCD求不出来

单点修改 有向图 没有标记 爽爆了!

修改x的父节点时需要讨论:

首先切掉原先的父节点

如果x是所在树的根 直接切掉special_father

如果x不是根,切掉x与父节点的联系,然后讨论x是否在环上

设x所在树的根节点为root

若root->special_father所在树的根不为root 则x在环上 将root的special_father变为root的fa节点

否则切断父节点完毕

然后讨论新的父节点f是否在x的子树中

若在,将x的special_father设为f

若不在,将x的fa设为f

好题……RE一晚上……居然忘了对LCT中的任意节点进行修改操作都要先Access+Splay了……居然直接把爸爸换了……RE到死啊

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 30300
#define p 10007
using namespace std;
struct line{
	int k,b;
	line operator + (const line &y) const
	{
		line re;
		re.k=k*y.k%p;
		re.b=(b*y.k+y.b)%p;
		return re;
	}
	int F(int x)
	{
		return (k*x+b)%p;
	}
};
struct abcd{
	abcd *ls,*rs,*fa,*sfa;
	line num,sum;
	abcd(int k,int b);
	void Push_Up();
}*null=new abcd(1,0),*tree[M];
abcd :: abcd(int k,int b)
{
	ls=rs=fa=sfa=null;
	num.k=sum.k=k;
	num.b=sum.b=b;
}
void abcd :: Push_Up()
{
	sum=ls->sum+num+rs->sum;
}
void Zig(abcd *x)
{
	abcd *y=x->fa;
	y->ls=x->rs;
	x->rs->fa=y;
	x->rs=y;
	x->fa=y->fa;
	if(y==y->fa->ls)
		y->fa->ls=x;
	else if(y==y->fa->rs)
		y->fa->rs=x;
	y->fa=x;
	y->Push_Up();
}
void Zag(abcd *x)
{
	abcd *y=x->fa;
	y->rs=x->ls;
	x->ls->fa=y;
	x->ls=y;
	x->fa=y->fa;
	if(y==y->fa->ls)
		y->fa->ls=x;
	else if(y==y->fa->rs)
		y->fa->rs=x;
	y->fa=x;
	y->Push_Up();
}
void Splay(abcd *x)
{
	while(x->fa->ls==x||x->fa->rs==x)
	{
		abcd *y=x->fa,*z=y->fa;
		if(x==y->ls)
		{
			if(y==z->ls)
				Zig(y);
			Zig(x);
		}
		else
		{
			if(y==z->rs)
				Zag(y);
			Zag(x);
		}
	}
	x->Push_Up();
}
void Access(abcd *x)
{
	abcd *y=null;
	while(x!=null)
	{
		Splay(x);
		x->rs=y;
		x->Push_Up();
		y=x;
		x=x->fa;
	}
}
abcd* Find_Root(abcd *x)
{
	abcd *y;
	Access(x);
	Splay(x);
	for(y=x;y->ls!=null;y=y->ls);
	return y;
}
int n,m,fa[M],v[M],tot;
pair<int,int>EXGCD(int x,int y)
{
	if(!y) return make_pair(1,0);
	pair<int,int>temp=EXGCD(y,x%y);
	return make_pair(temp.second,temp.first-x/y*temp.second);
}
int Inverse(int x)
{
	int temp=EXGCD((x+p)%p,p).first;
	return (temp%p+p)%p;
}
void DFS(int x)
{
	v[x]=tot;
	if(v[fa[x]]==tot)
	{
		tree[x]->sfa=tree[fa[x]];
		return ;
	}
	tree[x]->fa=tree[fa[x]];
	if(!v[fa[x]])
		DFS(fa[x]);
}
int Query(abcd *x)
{
	abcd *root=Find_Root(x);
	Access(root->sfa);
	Splay(root->sfa);
	int k=root->sfa->sum.k;
	int b=root->sfa->sum.b;
	if(k==1)
	{
		if(b==0)
			return -2;
		else
			return -1;
	}
	int temp=(p-b)*Inverse(k-1)%p;
	Access(x);
	Splay(x);
	return x->sum.F(temp);
}
void Modify(abcd *x,int k,abcd *f,int b)
{
	abcd *root=Find_Root(x);
	x->num.k=k;
	x->num.b=b;
	x->Push_Up();
	if(x==root)
		x->sfa=null;
	else
	{
		Access(x);
		Splay(x);
		x->ls->fa=null;
		x->ls=null;
		x->Push_Up();
		if(Find_Root(root->sfa)!=root)
		{
			Access(root);
			Splay(root);
			root->fa=root->sfa;
			root->sfa=null;
		}
	}
	Access(x);
	Splay(x);
	if(Find_Root(f)==x)
		x->sfa=f;
	else
		x->fa=f;
}
int main()
{
	int i,x,k,f,b;
	char o[10];
	cin>>n;
	for(i=1;i<=n;i++)
	{
		scanf("%d%d%d",&k,&f,&b);
		fa[i]=f;
		tree[i]=new abcd(k,b);
	}
	for(i=1;i<=n;i++)
		if(!v[i])
			++tot,DFS(i);
	cin>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%s",o);
		if(o[0]=='A')
			scanf("%d",&x),printf("%d\n", Query(tree[x]) );
		else
			scanf("%d%d%d%d",&x,&k,&f,&b),Modify(tree[x],k,tree[f],b);
	}
}


 

BZOJ 2759 一个动态树好题 Link-Cut-Tree+扩展欧几里得

标签:bzoj   bzoj2759   link-cut-tree   扩展欧几里得   数论   

原文地址:http://blog.csdn.net/popoqqq/article/details/40436165

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!