2016年8月25日 星期四

BZOJ 1798 1798: [Ahoi2009]Seq 維護序列seq

BZOJ 1798 1798: [Ahoi2009]Seq 維護序列seq

題目概述:
區間操作題。能支援區間加值,區間乘值,詢問區間總和。

思路:
這題我是先用線段樹來實作。
也附上treap版,但常數較大。
需要思考的是下放標記的方式。
下放加法就直接下放。
下放乘法應該要將子樹的add,mul都乘上。
這邊大概說明一下就是:
( a*b + c ) * d = a*b*d + c*d
然後要注意的是mul初始值應該要為1,若設為0會很麻煩的。
AC code:
//By SCJ
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
int n,p;
typedef long long ll;
struct Node{
   ll sum,add,mul;
   Node()
   {
       add=0;mul=1;
   }
}seg[600005];
inline ll all(int l,int r,int i)
{
   ll res=seg[i].sum%p;
   res*=(seg[i].mul%p);
   res%=p;
   return (res+seg[i].add*((r-l+1)%p)%p)%p;
}
inline void up(int l,int r,int i)
{
   int mid=(l+r)/2;
   seg[i].sum=(all(l,mid,i*2)+all(mid+1,r,i*2+1))%p;
}
inline void down(int l,int r,int i)
{
   seg[i].sum=all(l,r,i);
   ll ta=seg[i].mul,tb=seg[i].add;
   seg[i*2].mul=seg[i*2].mul*ta%p;
   seg[i*2+1].mul=seg[i*2+1].mul*ta%p;
   seg[i*2].add=(seg[i*2].add*ta%p + tb%p )%p;
   seg[i*2+1].add=(seg[i*2+1].add*ta%p + tb%p )%p;
   seg[i].add=0;
   seg[i].mul=1;
}

inline void build(int l,int r,int i)
{
   if(l==r)
   {
       int tp;//cin>>tp;
       scanf("%d",&tp);
       seg[i].sum=tp;
       return ;
   }
   int mid=(l+r)>>1;
   build(l,mid,i*2);
   build(mid+1,r,i*2+1);
   up(l,r,i);
}

inline void update(int l,int r,int i,int ql,int qr,int c,int type)
{
   if(r<ql||l>qr) return ;
   down(l,r,i);
   if(l>=ql&&r<=qr)
   {
       if(type==1) seg[i].mul=c;
       else seg[i].add=c;
       return ;
   }
   int mid=(l+r)/2;
   update(l,mid,i*2,ql,qr,c,type);
   update(mid+1,r,i*2+1,ql,qr,c,type);
   up(l,r,i);
}

inline int query(int l,int r,int i,int ql,int qr)
{
   if(r<ql||l>qr) return 0;
   down(l,r,i);
   if(l>=ql&&r<=qr) return all(l,r,i)%p;
   int mid=(l+r)>>1;
   return (query(l,mid,i*2,ql,qr)+query(mid+1,r,i*2+1,ql,qr))%p;
}

main()
{
   scanf("%d%d",&n,&p);
   build(1,n,1);
   int q;
   scanf("%d",&q);
   while(q--)
   {
       int type,a,b,c;//cin>>type;
       scanf("%d",&type);
       if(type==3)
       {
           scanf("%d%d",&a,&b);
           printf("%d\n",query(1,n,1,a,b));
       }
       else
       {
           scanf("%d%d%d",&a,&b,&c);
           update(1,n,1,a,b,c,type);
       }
   }
}


常數較大的treap版:
//By SCJ
#include<bits/stdc++.h>
//#include<random>
using namespace std;
#define endl '\n'
typedef long long ll;
struct treap;
treap *nul;
int n,p;
struct treap{
   ll val,sum,add,mul,sz,pr,pri;
   treap *l,*r;
   treap(int _val)
   {
       val=sum=_val;
       add=0;
       sz=mul=1;
       l=r=nul;
       pri=rand();
   }
   int all()
   {
       return ((sum*mul%p)+add*sz%p)%p;
   }
   void up()
   {
       sum=l->all()+r->all()+val;
       sz=l->sz+r->sz+1;
   }
   void down()
   {
       mul%=p;add%=p;
       sum=all();
       val=(val*mul%p+add)%p;
       l->mul=l->mul*mul%p;
       l->add=(l->add*mul%p + add )%p;
       r->mul=r->mul*mul%p;
       r->add=(r->add*mul%p + add )%p;
       add=0;mul=1;
   }
};
//mt19937 mt(56);
treap *merge(treap *a,treap *b)
{
   if(a==nul) return b;
   if(b==nul) return a;
//    if(rd()%(a->sz+b->sz)<=a->sz){
   if(a->pri<b->pri){
       a->down();
       a->r=merge(a->r,b);
       a->up();
       return a;
   }
   else{
       b->down();
       b->l=merge(a,b->l);
       b->up();
       return b;
   }
}

void split(treap *t,treap *&a,treap *&b,int k)
{
   if(t==nul) {a=b=nul;return ;}
   t->down();
   int asz=t->l->sz+1;
   if(asz<=k){
       a=t;
       split(t->r,a->r,b,k-asz);
   }
   else{
       b=t;
       split(t->l,a,b->l,k);
   }
   t->up();
}

int main()
{
   srand(12);
   nul=new treap(0);nul->sz=0;
   treap *root=nul;
   scanf("%d%d",&n,&p);
   for(int i=1;i<=n;++i)
   {
       int tp;
       scanf("%d",&tp);
       root=merge(root,new treap(tp));
   }
   int q;
   scanf("%d",&q);
   while(q--)
   {
       treap *tl=nul,*tr=nul;
       int type,a,b,c;
       scanf("%d",&type);
       if(type==1)
       {
           scanf("%d%d%d",&a,&b,&c);
           split(root,tl,root,a-1);
           split(root,root,tr,b-a+1);
           root->mul=root->mul*c%p;
           root->add=root->add*c%p;
           root=merge(merge(tl,root),tr);
       }
       else if(type==2)
       {
           scanf("%d%d%d",&a,&b,&c);
           split(root,tl,root,a-1);
           split(root,root,tr,b-a+1);
           root->add=(root->add+c)%p;
           root=merge(merge(tl,root),tr);
       }
       else
       {
           scanf("%d%d",&a,&b);
           split(root,tl,root,a-1);
           split(root,root,tr,b-a+1);
           printf("%d\n",root->all());
           root=merge(merge(tl,root),tr);
       }
   }
}



沒有留言:

張貼留言