时间管理,线段树,区间gcd修改,求和

时间管理

题意比较明显了。关键是维护gcd区间修改。
任老板直播的时候讲了:
在对一段区间做x变换后,下一次操作可能对区间内的数没有实质性的改变,比如如果区间内的数 全部变成1,无论什么数进行修改区间,区间的值不发生变化。具体怎么操作,维护了区间最大值和最小值,如果区间最大值=最小值=gcd(修改的值,最大值)那么说明本次修改对区间没有贡献,可以直接返回。然后指出这个算法不足之处比如对于2424242424242 这样的区间修改值=8虽然对区间没有贡献,但是最大值不等于最小值,所以依然要递归到叶子节点才能返回。
直播结束后任老板想到解决这个问题的方法,维护一个tag,tag记录该段区间的一个公倍数,对于某次修改如果修改值是tag的倍数说明,本次修改对区间无贡献。就可以return了
tag的维护:
对于某次修改后,该区间的一个公倍数为修改值,将这个修改值和原本的tag取gcd可以得到新的tag(小于等于原先的tag),区间全是1的tag=1,区间最大=最小=gcd的tag=最大

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<vector>
#include<functional>
using namespace std;
typedef long long LL;
inline LL read()
{
    LL kk=0,f=1;
    char cc=getchar();
    while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
    while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
    return kk*f;
}
const int maxn=124514;
struct segmentree//线段树我用的代码风格和蓝书(李煜东)的差不多
{
    int l,r;LL sum,tag,mi,ma,cnt;
    #define cnt(p) tre[p].cnt
    #define sum(p) tre[p].sum
    #define tag(p) tre[p].tag
    #define mi(p) tre[p].mi
    #define ma(p) tre[p].ma
    #define l(p) tre[p].l
    #define r(p) tre[p].r
}tre[maxn*4];
LL cun[maxn],n,m;
void update(int p)
{
    ma(p)=max(ma(p<<1),ma((p<<1)|1));
    mi(p)=min(mi(p<<1),mi((p<<1)|1));
    cnt(p)=cnt(p<<1)+cnt((p<<1)|1);
    sum(p)=sum(p<<1)+sum(p<<1|1);
}
void build(int p,int l,int r)
{
    l(p)=l;r(p)=r;
    LL mid=(l(p)+r(p))/2;
    if(l(p)==r(p))
    {
        tag(p)=mi(p)=ma(p)=sum(p)=cun[l];
        cnt(p)=(cun[l]==1);return;
    }
    build((p<<1),l,mid);build((p<<1)|1,mid+1,r);
    update(p);
}
void spread(int p)
{
    tag(p<<1)=tag(p);
    tag(p<<1|1)=tag(p);
}
void change(int p,int l,int r,LL val)
{
    if(l<=l(p)&&r(p)<=r)
    {
        if(tag(p))
		{
			if(val%tag(p)!=0)//更新tag
			{
				if(!tag(p))tag(p)=val;
				else tag(p)=__gcd(tag(p),val);
			}
			else return;//无贡献
		}
    }
    else spread(p);
    if(l(p)==r(p))
    {
        tag(p)=mi(p)=ma(p)=sum(p)=__gcd(sum(p),val);
        cnt(p)=(sum(p)==1);return;
    }
    if(cnt(p)==r(p)-l(p)+1)
    {
        tag(p)=1;
        return;
    }
    if(ma(p)==mi(p)&&__gcd(ma(p),val)==ma(p))
    {
        if(!tag(p))tag(p)=ma(p);
        else tag(p)=__gcd(tag(p),ma(p));
        return;
    }
    LL mid=(l(p)+r(p))/2;
    if(l<=mid)change((p<<1),l,r,val);
    if(r>mid)change((p<<1)|1,l,r,val);
    update(p);
}
LL query(int p,int l,int r)
{
    if(l<=l(p)&&r(p)<=r)
    {
        return sum(p);
    }
    LL mid=(l(p)+r(p))/2;
    LL kk=0;
    if(l<=mid)
    {
        kk+=query(p<<1,l,r);
    }
    if(r>mid)
    {
        kk+=query(p<<1|1,l,r);
    }
    return kk;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)cun[i]=read();
    build(1,1,n);
    while(m--)
    {
        int cc,x,y,z;cc=read();x=read();y=read();
        if(cc==1)
        {
            z=read();
            change(1,x,y,z);
        }
        else printf("%lld\n",query(1,x,y));
    }
}

实际上tag写的好可以不用再维护最大值和最小值,将更新操作加上tag的更新,如果子区间tag值一致则更新,否则变成0
任老板官方修改后:

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<vector>
#include<functional>
using namespace std;
typedef long long LL;
inline LL read()
{
    LL kk=0,f=1;
    char cc=getchar();
    while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
    while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
    return kk*f;
}
const int maxn=124514;
struct segmentree
{
	int l,r;LL sum,tag,cnt;
	#define cnt(p) tre[p].cnt
	#define sum(p) tre[p].sum
	#define tag(p) tre[p].tag
	#define l(p) tre[p].l
	#define r(p) tre[p].r
}tre[maxn*4];
LL cun[maxn],n,m;
void update(int p)
{
	cnt(p)=cnt(p<<1)+cnt((p<<1)|1);
	sum(p)=sum(p<<1)+sum(p<<1|1);
	if(tag(p<<1)==tag(p<<1|1))tag(p)=tag(p<<1);
	else tag(p)=0;
}
void build(int p,int l,int r)
{
	l(p)=l;r(p)=r;
	LL mid=(l(p)+r(p))/2;
	if(l(p)==r(p))
	{
		tag(p)=sum(p)=cun[l];
		cnt(p)=(cun[l]==1);return;
	}
	build((p<<1),l,mid);build((p<<1)|1,mid+1,r);
	update(p);
}
void change(int p,int l,int r,LL val)
{
	if(l(p)==r(p))
	{
		tag(p)=sum(p)=__gcd(sum(p),val);
		cnt(p)=(sum(p)==1);return;
	}
	if(l<=l(p)&&r(p)<=r)
	{
		if(tag(p))
		{
			if(val%tag(p)!=0)
			{
				if(!tag(p))tag(p)=val;
				else tag(p)=__gcd(tag(p),val);
			}
			else return;
		}
	}
	LL mid=(l(p)+r(p))/2;
	if(l<=mid)change((p<<1),l,r,val);
	if(r>mid)change((p<<1)|1,l,r,val);
	update(p);
}
LL query(int p,int l,int r)
{
	if(l<=l(p)&&r(p)<=r)
	{
		return sum(p);
	}
	LL mid=(l(p)+r(p))/2;
	LL kk=0;
	if(l<=mid)
	{
		kk+=query(p<<1,l,r);
	}
	if(r>mid)
	{
		kk+=query(p<<1|1,l,r);
	}
	return kk;
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;++i)cun[i]=read();
	build(1,1,n);
	while(m--)
	{
		int cc,x,y,z;cc=read();x=read();y=read();
		if(cc==1)
		{
			z=read();
			change(1,x,y,z);
		}
		else printf("%lld\n",query(1,x,y));
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值