时间管理
题意比较明显了。关键是维护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));
}
}