刷题记录:HDU - 4902 Nice boat 区间修改+区间gcd

传送门:Vjudge

题目描述:

There is a hard data structure problem in the contest:
There are n numbers a_1,a_2,...,a_n on a line, everytime you can change every number in a 
segment [l,r] into a number x(type 1), or change every number a_i in a segment [l,r] which is 
bigger than x to gcd(a_i,x) (type 2).
You should output the final sequence.
输入:
1
10
16807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709 
10
1 3 6 74243042
2 4 8 16531729
1 3 4 1474833169
2 1 8 1131570933
2 7 9 1505795335
2 3 7 101929267
1 4 10 1624379149
2 2 8 2110010672
2 6 7 156091745
1 2 5 937186357
输出:
16807 937186357 937186357 937186357 937186357 1 1 1624379149 1624379149 1624379149 

一道诈骗题??

刚开始看到这道题很显然会发现这是一道区间修改+区间查询的题目.应该是由线段树进行操作的.但是看到区间gcd的时候,我想了一下.显然对于我们的区间修改来说,我们可以打一个 l a z y _ t a g lazy\_tag lazy_tag即可

当我们进行区间gcd的时候,我们会发现我们需要区间的最大值(这样可以很好的进行剪枝操作),所以我们可以维护区间最大值.假设此时我们的区间存在 l a z y _ t a g lazy\_tag lazy_tag,那么相当于这个区间所有的数字都是相同的,此时我们进行区间gcd十分的容易,直接改一下当前根的值以及lazy_tag的值即可.

但是刚开始让我陷入疑问的是当我们没有 l a z y _ t a g lazy\_tag lazy_tag的时候,我们进行区间gcd的话就意味着需要逐个逐个进行单点修改,就是 n n n的复杂度,这样的话假设我们所有的修改都是区间gcd的话,线段树的复杂度可能达到 n ∗ q n*q nq,似乎会被卡??

然而我们会发现一个有意思的点就是,我们进行区间gcd需要满足当前的区间的最大值大于我们的x.对于 g c d ( a , b ) ( a > b ) gcd(a,b)(a>b) gcd(a,b)(a>b),存在这样的一个规律, g c d ( a , b ) < a / 2 gcd(a,b)<a/2 gcd(a,b)<a/2.并且我们的数字最大是 2 32 2^{32} 232,这就意味着我们一直进行gcd操作最多也只需要32次(实际上没有32次).就变成了1,对于1来说,再进行gcd操作就可以直接剪枝掉了.所以我们的复杂度就可以优化了很大一部分(完整的均摊复杂度我不会算,但是可以凭感觉想一下QAQ).并且此题的时限为 1.5 s 1.5s 1.5s.所以线段树还是能通过本题的.

下面是具体的代码部分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 100010
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
struct Segment_tree{
	int l,r,mx,lazy;
}tree[maxn*4];
int n,m;int a[maxn];
int gcd(int aa,int bb) {
	if(aa%bb==0) return bb;
	else return gcd(bb,aa%bb);
}
void build(int l,int r,int rt) {
	tree[rt].l=l;tree[rt].r=r;
	if(l==r) {
		tree[rt].mx=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(lson);build(rson);
	tree[rt].mx=max(tree[ls].mx,tree[rs].mx);
}
void pushdown(int rt) {
	tree[ls].lazy=tree[ls].mx=tree[rt].lazy;
	tree[rs].lazy=tree[rs].mx=tree[rt].lazy;
	tree[rt].lazy=0;
}
void update1(int l,int r,int rt,int v) {
	if(tree[rt].l==l&&tree[rt].r==r) {
		tree[rt].lazy=tree[rt].mx=v;
		return ;
	}
	if(tree[rt].lazy) pushdown(rt);
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if(r<=mid) update1(l,r,ls,v);
	else if(l>mid) update1(l,r,rs,v);
	else update1(l,mid,ls,v),update1(mid+1,r,rs,v);
	tree[rt].mx=max(tree[ls].mx,tree[rs].mx);
}
void update2(int l,int r,int rt,int v) {
	if(tree[rt].mx<=v) return ;
	if(tree[rt].l==l&&tree[rt].r==r) {
		if(tree[rt].lazy) {
			tree[rt].lazy=tree[rt].mx=gcd(tree[rt].mx,v);
			return ;
		}else {
			if(tree[rt].l==tree[rt].r) {
				tree[rt].lazy=tree[rt].mx=gcd(tree[rt].mx,v);
				return ;
			}
		}
	}
	if(tree[rt].lazy) pushdown(rt);
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if(r<=mid) update2(l,r,ls,v);
	else if(l>mid) update2(l,r,rs,v);
	else update2(l,mid,ls,v),update2(mid+1,r,rs,v);
	tree[rt].mx=max(tree[ls].mx,tree[rs].mx);
}
void query(int l,int r,int rt) {
	if(tree[rt].l==tree[rt].r) {
		printf("%d ",tree[rt].mx);
		return ;
	}
	if(tree[rt].lazy) pushdown(rt);
	int mid=(l+r)>>1;
	query(lson);query(rson);
}
int main() {
	int T;T=read();
	while(T--) {
		memset(tree,0,sizeof(tree));
		n=read();
		for(int i=1;i<=n;i++) a[i]=read();
		build(root);
		m=read();
		for(int i=1;i<=m;i++) {
			int opt=read();
			if(opt==1) {
				int l=read(),r=read(),x=read();
				update1(l,r,1,x);
			}else {
				int l=read(),r=read(),x=read();
				update2(l,r,1,x);
			}
		}
		query(root);
		cout<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值