bzoj 1798 [Ahoi2009]Seq 维护序列seq ——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1798

先乘后加,就可给加法标记乘上乘法标记。

注意可能有 *0 的操作,所以 pshd 时不是 cg[ cr ]>1 而是 cg[ cr ]!=1 。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define ls Ls[cr]
#define rs Rs[cr]
using namespace std;
const int N=1e5+5,M=N<<1;
int n,m,mod,a[N],Ls[M],Rs[M],cg[M],jg[M],sm[M],tot;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();}
  while(ch>=‘0‘&&ch<=‘9‘) ret=(ret<<3)+(ret<<1)+ch-‘0‘,ch=getchar();
  return fx?ret:-ret;
}
int g[20];
void wrt(int x)
{
  if(x<0)putchar(‘-‘),x=-x;
  if(!x){puts("0");return;}
  int t=0;while(x)g[++t]=x%10,x/=10;
  while(t)putchar(g[t]+‘0‘),t--;puts("");
}
void upd(int &x){x>=mod?x-=mod:0;}
void pshp(int cr){sm[cr]=sm[ls]+sm[rs];upd(sm[cr]);}
void build(int l,int r,int cr)
{
  cg[cr]=1;
  if(l==r){sm[cr]=a[l]%mod;return;}
  int mid=l+r>>1;
  ls=++tot; build(l,mid,ls);
  rs=++tot; build(mid+1,r,rs);
  pshp(cr);
}
void pshd(int cr,int l,int mid,int r)
{
  if(cg[cr]!=1)
    {
      int w=cg[cr]; cg[cr]=1;
      cg[ls]=(ll)cg[ls]*w%mod; cg[rs]=(ll)cg[rs]*w%mod;
      jg[ls]=(ll)jg[ls]*w%mod; jg[rs]=(ll)jg[rs]*w%mod;
      sm[ls]=(ll)sm[ls]*w%mod; sm[rs]=(ll)sm[rs]*w%mod;
    }
  if(jg[cr])
    {
      int w=jg[cr]; jg[cr]=0;
      jg[ls]+=w;upd(jg[ls]); jg[rs]+=w;upd(jg[rs]);
      sm[ls]=(sm[ls]+(ll)(mid-l+1)*w)%mod; sm[rs]=(sm[rs]+(ll)(r-mid)*w)%mod;
    }
}
void mdfy(int l,int r,int cr,int L,int R,int k,bool fx)
{
  if(l>=L&&r<=R)
    {
      if(!fx)
    cg[cr]=(ll)cg[cr]*k%mod, sm[cr]=(ll)sm[cr]*k%mod, jg[cr]=(ll)jg[cr]*k%mod;
      else
    jg[cr]+=k,upd(jg[cr]),sm[cr]=(sm[cr]+(ll)(r-l+1)*k)%mod;
      return;
    }
  int mid=l+r>>1; pshd(cr,l,mid,r);
  if(L<=mid)mdfy(l,mid,ls,L,R,k,fx);
  if(mid<R)mdfy(mid+1,r,rs,L,R,k,fx);
  pshp(cr);
}
int query(int l,int r,int cr,int L,int R)
{
  if(l>=L&&r<=R)return sm[cr];
  int mid=l+r>>1,ret=0; pshd(cr,l,mid,r);
  if(L<=mid)ret=query(l,mid,ls,L,R);
  if(mid<R)ret+=query(mid+1,r,rs,L,R),upd(ret);
  return ret;
}
int main()
{
  n=rdn(); mod=rdn();
  for(int i=1;i<=n;i++)a[i]=rdn();
  tot=1; build(1,n,1);
  m=rdn();
  for(int i=1,op,l,r,k;i<=m;i++)
    {
      op=rdn();l=rdn();r=rdn();
      if(op==1)
    {
      k=rdn()%mod;mdfy(1,n,1,l,r,k,0);
    }
      if(op==2)
    {
      k=rdn()%mod;mdfy(1,n,1,l,r,k,1);
    }
      if(op==3)
    wrt(query(1,n,1,l,r));
    }
  return 0;
}

原文地址:https://www.cnblogs.com/Narh/p/9887199.html

时间: 2024-11-05 18:40:30

bzoj 1798 [Ahoi2009]Seq 维护序列seq ——线段树的相关文章

BZOJ 1798 Ahoi 2009 维护序列seq

题目大意:维护一个序列,能够区间加,区间乘,然后取区间和模一个数的值. 思路:线段树维护一个有两个域的标记,一个表示加,一个表示乘.下传的时候一起下传,先乘后加. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define MO p #define LEFT (pos << 1) #de

bzoj 1798: [Ahoi2009]Seq 维护序列seq 线段树 区间乘法区间加法 区间求和

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1798 Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列

1798: [Ahoi2009]Seq 维护序列seq

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 5886  Solved: 2087[Submit][Status][Discuss] Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和

bzoj1798: [Ahoi2009]Seq 维护序列seq(线段树多重标记下传)

www.cnblogs.com/shaokele/ bzoj1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec Memory Limit: 64 MB Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值

bzoj1798: [Ahoi2009]Seq 维护序列seq 2011-12-20

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MB Submit: 497  Solved: 203 [Submit][Status][Discuss] Description 老 师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的

bzoj1798[Ahoi2009]Seq 维护序列seq

bzoj1798[Ahoi2009]Seq 维护序列seq 题意: 维护序列,支持区间加.区间乘.区间求和模一个数.序列大小和操作数≤100000 题解: 线段树,加标记和乘标记的处理同bzoj4003.模的时候注意细节. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define m

bzoj 1798 [Ahoi2009]Seq 维护序列seq(线段树+传标)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1798 [题意] 给定一个序列,要求提供区间乘/加,以及区间求和的操作 [思路] 线段树+传标. 下传标记的方式可以类比这里 click here [代码] 1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio>

BZOJ 1798 [Ahoi2009]Seq 维护序列seq 线段树

题意:链接 方法:线段树 解析: 俩标记sb题 更新乘的时候更新加 完了 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 #define N 100010 using namespace std; typedef

【BZOJ 1798】 [Ahoi2009]Seq 维护序列seq

Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. Input 第一行两个整数N和P(1≤P≤1000000000).第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N).第三行有一个整

bzoj 1798 [Ahoi2009]Seq 维护序列seq

原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1798 线段树区间更新: 1. 区间同同时加上一个数 2. 区间同时乘以一个数 1 #include<algorithm> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstdio> 5 #define lc root<<1 6 #define rc root<<1|1