【树上gcd】codechef DGCD

35 篇文章 0 订阅
5 篇文章 0 订阅

用更相减损术的思想化为维护差分就很容易做了,lct与树链剖分均可,边界要注意,中间直接弄成开区间

codechef常数也卡得很厉害...

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
int a[200000],b[200000],gcc[200000],ad[200000],rt[200000],l[200000],r[200000],d[200000],st[200000];
int n,q,ans,next[500000],sora[500000],tail[500000],ss;
inline void link(int x,int y)
{
	++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y;
	++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x;
}
void bfs(int s)
{
	memset(d,127,sizeof(d));
	int h,r=h=0;
	st[r=1]=s,d[s]=0,b[s]=gcc[s]=a[s];
	for (;h<r;) {
		int ne=st[++h],na;
		for (int i=ne;next[i];) {
			i=next[i],na=sora[i];
			if (d[ne]+1<d[na]) {
				d[na]=d[ne]+1;
				st[++r]=na;
				rt[na]=ne,b[na]=a[na]-a[ne],gcc[na]=abs(b[na]);
			}
		}
	}
}
inline int gcd(int a,int b)
{
	a=abs(a),b=abs(b);
	if (!a) return b;
	if (!b) return a;
	for (;a%b;) {
		int t=a%b;
		a=b;
		b=t;
	}
	return b;
}
inline void updata(int x)
{
	gcc[x]=gcd(gcc[l[x]],gcc[r[x]]);
	gcc[x]=gcd(gcc[x],b[x]);
}
inline void add(int x,int w)
{
	if (!x) return ;
	a[x]+=w;
	ad[x]+=w;
}
inline void pushdown(int x)
{
	if (!x) return ;
	if (ad[x]) add(l[x],ad[x]),add(r[x],ad[x]);
	ad[x]=0;
}
inline void left(int x) 
{
	int y=rt[x],z=rt[y];
	r[y]=l[x],rt[l[x]]=y;updata(y);
	l[x]=y,rt[y]=x;
	if (l[z]==y) l[z]=x;else if (r[z]==y) r[z]=x;rt[x]=z;	
}
inline void right(int x)
{
	int y=rt[x],z=rt[y];
	l[y]=r[x],rt[r[x]]=y;updata(y);
	r[x]=y,rt[y]=x;
	if (l[z]==y) l[z]=x;else if (r[z]==y) r[z]=x;rt[x]=z;
}
inline void splay(int x)
{
	pushdown(x);
	for (;l[rt[x]]==x || r[rt[x]]==x;) {
		int y=rt[x],z=rt[y];
		if (l[z]==y || r[z]==y) pushdown(z);pushdown(y);pushdown(x);
		if (l[y]==x) {
			if (l[z]==y) right(y);
			right(x);
		} 
		else {
			if (r[z]==y) left(y);
			left(x);
		}
	}
	updata(x);
}
inline void access(int x)
{
	int xx=x;
	splay(x);
	l[x]=0,updata(x);
	for (;rt[x];) {
		int y=rt[x],z;
		splay(y);
		for (z=x;r[z];z=r[z]) ;
		splay(z);
		b[z]=a[z]-a[y],updata(z);
		l[y]=z,updata(y);
		x=y;
	}
	splay(xx);
}
void origin()
{
	ss=n;
	for (int i=1;i<=n;i++) tail[i]=i;
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
		scanf("%d\n",&n);
		origin();
		for (int i=1;i<=n-1;i++) {
			int x,y;
			scanf("%d%d\n",&x,&y);
			x++,y++;
			link(x,y);
		}
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);scanf("\n");
		bfs(1);
		cin>>q;
		scanf("\n");
		for (int i=1;i<=q;i++) {
			int root,x,y,z;
			char ch;
			scanf("%c",&ch);
			//printf("%d\n",clock());
			if ('C'==ch) {
				scanf("%d%d%d\n",&x,&y,&z);x++,y++;
				if (!z) continue;
				if (x==y) {
					splay(x);
					a[x]+=z,b[x]+=z;updata(x);
					continue;
				}
 				access(x),access(y);
				splay(x),root=rt[x];
				if (!root) root=x;
				access(root);
				if (root!=x) splay(x),add(x,z);
				if (root!=y) splay(y),add(y,z);
				a[root]+=z,b[root]+=z,updata(root);
			}
			else {
				scanf("%d%d\n",&x,&y);x++,y++;
				if (x==y) {
					splay(x);
					printf("%d\n",a[x]);
					continue;
				}
				access(x),access(y);
				splay(x),root=rt[x];
				if (!root) root=x;
				access(root);
				ans=0;
				if (root!=x) splay(x),ans=gcd(ans,gcc[x]),ans=gcd(ans,a[x]);
				if (root!=y) splay(y),ans=gcd(ans,gcc[y]),ans=gcd(ans,a[y]);
				ans=gcd(ans,a[root]);
				printf("%d\n",ans);
			}
		}
		//if (clock()>1000) printf("1\n");
		//printf("%d\n",clock());
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值