CodeForces438D The Child and Sequence(线段树取模)

简化版题意:
给定一个长度为n的非负整数序列a,你需要支持以下操作:
1:给定l,r,输出a[l]+a[l+1]+…+a[r]。
2:给定l,r,x,将a[l],a[l+1],…,a[r]对x取模。
3:给定k,y,将a[k]修改为y。
n,m<=100000,a[i],x,y<=109。

用线段树维护区间最大值以及区间和。
进行取模操作时,如果x>区间最大值那么退出,否则两边都递归下去。
单个数被有效地取模一次只会花费O(logn)的时间,并且数值至少减半,因此每次修改至多使时间复杂度增加O(lognlogW)。
时间复杂度O(mlognlogW)

#include
#include
#include
#include
#define ll long long 
 using namespace std;
const int MAXN=1000100;
struct JKX{
  int l,r,maxx;
  ll  w;
}t[MAXN*4+100];
int n,T;
void upto(int k)
{
   t[k].w=t[k*2].w+t[k*2+1].w;
   t[k].maxx=max(t[k*2].maxx,t[k*2+1].maxx);
}
void build(int k,int l,int r)
{
	t[k].l=l; t[k].r=r;
	if(l==r) { scanf("%lld",&t[k].w);  t[k].maxx=t[k].w; return;}
	int mid=(l+r)/2; 
	build(k*2,l,mid);
	build(k*2+1,mid+1,r);
    upto(k);
}
void change2(int k,int x,int v)
{
  if(t[k].l==t[k].r) { t[k].w=v; t[k].maxx=v; return;}
  int mid=(t[k].l+t[k].r)/2;
  if(x<=mid) change2(k*2,x,v);
   else change2(k*2+1,x,v);
  upto(k);
}
void change1(int k,int l,int r,int mod)
{
  if(l<=t[k].l && r>=t[k].r && mod>t[k].maxx) return;
  if(t[k].l==t[k].r) {t[k].w%=mod; t[k].maxx=t[k].w; return;}
  int mid=(t[k].l+t[k].r)/2;
  if(l<=mid) change1(k*2,l,r,mod);
   if(r>mid) change1(k*2+1,l,r,mod);
  upto(k);
}
ll get(int k,int l,int r)
{
	if(l<=t[k].l && r>=t[k].r) return (ll)t[k].w;
	int mid=(t[k].l+t[k].r)/2;
	long long J=0;
	if(l<=mid) J+=get(k*2,l,r);
	 if(r>mid) J+=get(k*2+1,l,r);
	return J;
}
int main()
{ 
  scanf("%d%d",&n,&T);
  build(1,1,n);
  int opt,l,r,mod;
  for(int i=1;i<=T;i++)
  { 
    scanf("%d",&opt);
    if(opt==1) { scanf("%d%d",&l,&r); printf("%lld\n",get(1,l,r)); }
    if(opt==2) { scanf("%d%d%d",&l,&r,&mod); change1(1,l,r,mod);}
    if(opt==3) { scanf("%d%d",&l,&r);  change2(1,l,r);}
  }
  return 0;	
} 

类似问题区间开方 在固定次数后区间全变成1 就不用修改了。
题目 hluoj 680
分块版本:

#include
 using namespace std;
const int MAXN=50050;
int a[MAXN],f[MAXN],bl[MAXN],sum[MAXN];
int n,blo;
void change(int l,int r)
{   
  if(!f[bl[l]]) 
   {
   	for(int i=l;i<=min(r,bl[l]*blo);i++)  sum[bl[l]]-=(a[i]-sqrt(a[i])),a[i]=sqrt(a[i]);
   }
  if(!f[bl[r]] && bl[r]!=bl[l])
   {
   	for(int i=(bl[r]-1)*blo+1;i<=r;i++) sum[bl[r]]-=(a[i]-sqrt(a[i])),a[i]=sqrt(a[i]);
   }
  for(int i=bl[l]+1;i<=bl[r]-1;i++)
  {
  	if(f[i]) continue;
  	sum[i]=0; f[i]=1;
  	for(int j=(i-1)*blo+1;j<=i*blo;j++) 
  	 {
  	 	a[j]=sqrt(a[j]),sum[i]+=a[j];
  	   	if(a[j]>1) f[i]=0; 
	 }
  }
}
int get(int l,int r)
{
	int J=0;
	for(int i=l;i<=min(r,bl[l]*blo);i++) J+=a[i];
	if(bl[l]!=bl[r])
	 for(int i=(bl[r]-1)*blo+1;i<=r;i++) J+=a[i];
	for(int i=bl[l]+1;i<=bl[r]-1;i++)  J+=sum[i]; 
    return J;
}
int main()
{
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);	
  cin>>n; blo=sqrt(n);
  for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  for(int i=1;i<=n;i++) bl[i]=(i-1)/blo+1,sum[bl[i]]+=a[i];
  int opt,l,r,c;
  for(int i=1;i<=n;i++)
  {
    scanf("%d%d%d%d",&opt,&l,&r,&c);
    if(opt==0) change(l,r);
    if(opt==1) printf("%d\n",get(l,r));
  }
  return 0;
}

线段树版本有一题 BZOJ 3211 花神游历各国 还没写过(哭)
还有区间取对数什么的 , 做到题目再写(开坑)

你可能感兴趣的:(线段树)