[LCT 线性模方程] BZOJ 2759 一个动态树好题 & 4266 小强的动态方程

19 篇文章 0 订阅

%%%PoPoQQQ:http://blog.csdn.net/popoqqq/article/details/40436165


题目大意:给定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”


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define P 10007 
using namespace std;
typedef long long ll;
typedef pair<ll,ll> abcd;

inline char nc()
{
	static char buf[100000],*p1=buf,*p2=buf;
	if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
	return *p1++;
}

inline void read(int &x)
{
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline void read(ll &x)
{
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline void read(char &x){
	for (x=nc();x!='A' && x!='C';x=nc());
}

namespace Math{
	#define N 10007
	inline abcd EXGCD(ll a,ll b){
		if (a<b) { abcd ret=EXGCD(b,a); return abcd(ret.second,ret.first); }
		if (!b) return abcd(1,0);
		abcd ret=EXGCD(b,a%b);
		return abcd(ret.second,ret.first-a/b*ret.second);
	}
	inline int Solve(ll A,ll B){
		A%=N; B%=N;
		if (!A && !B) return -2;
		if (!A && B) return -1;
		if (!B && A) return 0;
		abcd E=EXGCD(A,N);
		ll D=A*E.first+N*E.second,X;
		if (B%D==0)
		{
			if (D!=1) return -2;
			X=E.first*(B/D);
			X=((X%(N/D))+N/D)%(N/D);
			return X;
		}
		else
			return -1;
	}
}

struct data{
	ll k,b;
	data(int k=1,int b=0):k(k),b(b) { }
	friend data operator + (const data &A,const data &B){
		data ret;
		(ret.k=A.k*B.k)%=P;
		(ret.b=B.k*A.b+B.b)%=P;
		return ret;
	}
};

struct Splay{
	struct node{
		int size,idx;
		node *p,*ch[2],*fat,*sf;
		data dat,sum;
		inline void setc(node *c,int d) { ch[d]=c; c->p=this; }
		inline bool dir() { return p->ch[1]==this; }
		inline void update() { size=ch[1]->size+ch[0]->size+1; sum=ch[0]->sum+dat+ch[1]->sum; }
	}*null,Mem[30005];
	Splay() { null=Mem; null->p=null->ch[0]=null->ch[1]=null->fat=null->sf=null; null->size=0; null->sum=data(1,0); }
	inline void rot(node *x){
		if (x==null || x->p==null) return;
		bool d=x->dir(); node *p=x->p;
		if (p->p!=null) p->p->setc(x,p->dir()); else x->p=null;
		p->setc(x->ch[d^1],d); x->setc(p,d^1); p->update(); x->update(); swap(x->fat,p->fat);
	}
	inline void splay(node *x){
		if (x==null) return;
		while (x->p!=null)
			if (x->p->p==null)
				rot(x);
			else
				x->dir()==x->p->dir()?(rot(x->p),rot(x)):(rot(x),rot(x));
	}
	inline node *Access(node *x){
		node *y=null;
		while (x!=null)
		{
			splay(x);
			x->ch[1]->p=null; x->ch[1]->fat=x;
			x->setc(y,1); y->fat=null;
			x->update();
			y=x; x=x->fat;
		}
		return y;
	}
	inline void Link(node *x,node *y){
		Access(x);
		splay(x);
		x->fat=y;
		Access(x);
	}
	inline void Cut(node *x){
		Access(x);
		splay(x);
		x->ch[0]->p=null; x->ch[0]=null;
		x->fat=null; x->update();
	}
	inline node *Root(node *x){
		Access(x);
		splay(x);
		node *y=x;
		while (y->ch[0]!=null) y=y->ch[0];
		return y;
	}
	inline bool Jud(node *x,node *y){
		return Root(x)==Root(y);
	}
	inline data Query(node *x){
		return Access(x)->sum;
	}
}LCT;

int n;
Splay::node *pos[30005];

inline void Init(int n)
{
	for (int i=1;i<=n;i++)
		pos[i]=LCT.Mem+i,pos[i]->p=pos[i]->ch[0]=pos[i]->ch[1]=pos[i]->fat=pos[i]->sf=LCT.null,pos[i]->size=1,pos[i]->idx=i;
}

inline void PCut(Splay::node *x){
	if (x==LCT.Root(x)){
		x->sf=LCT.null; return;
	}
	Splay::node *rt=LCT.Root(x),*sf=rt->sf;
	LCT.Cut(x);
	if (!LCT.Jud(sf,rt))
	{
		rt->sf=LCT.null;
		LCT.Link(rt,sf);	
	}
}

inline void PLink(Splay::node *x,Splay::node *y){
	if (LCT.Root(y)==x){
		x->sf=y; return;
	}
	LCT.Link(x,y);
}

int par[30005];

int main()
{
	char order;
	int ip,Q,u,ik,ib;
	ll x0;
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	read(n); Init(n);
	for (int i=1;i<=n;i++)
	{
		read(pos[i]->dat.k); read(par[i]); read(pos[i]->dat.b); pos[i]->update();
	}
	for (int i=1;i<=n;i++)
		PLink(pos[i],pos[par[i]]);
	read(Q);
	while (Q--)
	{
		read(order);
		if (order=='A')
		{
			read(u);
			Splay::node *sf=LCT.Root(pos[u])->sf;
			data ret=LCT.Query(sf); 
			ret.b=(-ret.b+P)%P;
			ret.k=(ret.k-1+P)%P;
			x0=Math::Solve(ret.k,ret.b);
			if (x0!=-1 && x0!=-2)
			{
				ret=LCT.Query(pos[u]);
				(x0=x0*ret.k+ret.b)%=P;
			}
			printf("%lld\n",x0);
		}
		else
		{
			read(u); read(ik); read(ip); read(ib);
			PCut(pos[u]);
			LCT.splay(pos[u]);
			pos[u]->dat=data(ik,ib); pos[u]->update();
			PLink(pos[u],pos[ip]);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值