[HDOJ 4902] Nice boat [线段树]

69 篇文章 0 订阅
27 篇文章 0 订阅

给定数列,有2种操作,分别是区间内所有数置成一个数x,区间内所有大于x的数和x求gcd。输出所有操作完成后的数列。

操作和数列长度均为10^5。

建立线段树,记录区间内是否所有元素的值都一样。如果一样就直接求gcd,否则查看子区间。

复杂度感觉上应该是m*logn*logn的。因为一个数最多求log次gcd就会变成1,然后大家就都一样了。区间修改的话最多分成log个区间。具体不会算...

#include <cstdio>

inline int gcd(int x,int y) {
	if (x==0) return y;
	if (y==0) return x;
	int z=x%y;
	while (z) {
		x=y;y=z;z=x%y;
	}
	return y;
}

int n,x;
int a[100001];

struct SeqNode {
	SeqNode *ls,*rs;
	int v;
	void down() {
		if (v!=-1) ls->v=rs->v=v;
	}
	void repair() {
		if (ls->v==rs->v) v=ls->v;
		else v=-1;
	}
};
SeqNode b[200000],*bp,*root;

SeqNode *maketree(int l,int r) {
	SeqNode *ans=bp++;
	if (l==r) {
		ans->ls=ans->rs=NULL;
		ans->v=a[l];
	} else {
		int t=(l+r)/2;
		ans->ls=maketree(l,t);
		ans->rs=maketree(t+1,r);
		ans->repair();
	}
	return ans;
}
void set1(SeqNode *from,int l,int r,int ll,int rr) {
	if (l==ll&&r==rr) {
		from->v=x;
	} else {
		int t=(l+r)/2;
		from->down();
		if (rr<=t) set1(from->ls,l,t,ll,rr);
		else if (ll>t) set1(from->rs,t+1,r,ll,rr);
		else {
			set1(from->ls,l,t,ll,t);
			set1(from->rs,t+1,r,t+1,rr);
		}
		from->repair();
	}
}
void set2(SeqNode *from,int l,int r,int ll,int rr) {
	if (l==ll&&r==rr&&from->v!=-1) {
		if (from->v>x) from->v=gcd(from->v,x);
	} else {
		int t=(l+r)/2;
		from->down();
		if (rr<=t) set2(from->ls,l,t,ll,rr);
		else if (ll>t) set2(from->rs,t+1,r,ll,rr);
		else {
			set2(from->ls,l,t,ll,t);
			set2(from->rs,t+1,r,t+1,rr);
		}
		from->repair();
	}
}
void godown(SeqNode *from,int l,int r) {
	if (l==r) a[l]=from->v;
	else {
		int t=(l+r)/2;
		from->down();
		godown(from->ls,l,t);
		godown(from->rs,t+1,r);
	}
}

int main() {
	int t,q,i;
	scanf("%d",&t);
	while (t--) {
		scanf("%d",&n);
		for (i=1;i<=n;i++) scanf("%d",&a[i]);
		bp=b;
		root=maketree(1,n);
		scanf("%d",&q);
		while (q--) {
			int t,l,r;
			scanf("%d%d%d%d",&t,&l,&r,&x);
			if (t==1) set1(root,1,n,l,r);
			else set2(root,1,n,l,r);
		}
		godown(root,1,n);
		for (i=1;i<=n;i++) printf("%d ",a[i]);
		printf("\n");
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值