【NOIP2013模拟联考7】数列

Description

给出一个序列,每个数由一个二元组(a,b)表示,有4中操作
1:把a值在l~r范围内的数乘上x再加上y
2:把b值在l~r范围内的数乘上x再加上y
3:询问a值在l~r范围内的数的和。
4:询问b值在l~r范围内的数的和。
n<=50000

Solution

写了一下午的常数优化,终于过掉了TAT
具体来说,很容易想到第一个分块。我们用每个块维护原块和排过序的块,那么我们2操作修改的就是每个块中的连续一段。1操作所覆盖的块由于对应关系可以直接打标记,剩下的自己手动修改就好了。
那么连续的区间标记呢?总不能用线段树吧?
好吧,一开始我用的就是线段树。。。结果跑的和暴力分数一样(出题人良心大大滴坏TAT)
其实,我们可以使用分块套分块(分块大法好!)
区间标记也用一个块来维护。
然后?然后你就去打吧。。。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 50005
#define M 2500
#define S 50
using namespace std;
const int mo=536870912;
struct note{int v,w;}a[N],b[N];
bool cmp(note x,note y) {return x.v<y.v;}
int L[M],R[M],Len[M],l[M][S],r[M][S],sz[M],len[M][S],pos[N],n,m,q,op,x,y,u,v;
int Sum[M],Mul[M],Add[M],sum[M][S],mul[M][S],add[M][S],p[N],ans,Mult,Plus;
int get() {
    char ch;while (!isdigit(ch=getchar()));
    int o=ch-48;while (isdigit(ch=getchar())) o=o*10+ch-48;
    return o;
}
void make(int x) {
    sz[x]=Len[x]/S+((Len[x]%S)>0);
    fo(i,1,sz[x]) {
        l[x][i]=max(r[x][i-1]+1,L[x]);
        r[x][i]=min(l[x][i]+S,R[x]);
        len[x][i]=r[x][i]-l[x][i]+1;
        mul[x][i]=1;
    }
}
void Down(int x) {
    (Sum[x]*=Mul[x])+=Len[x]*Add[x];
    fo(i,1,sz[x]) mul[x][i]*=Mul[x],(add[x][i]*=Mul[x])+=Add[x];
    Mul[x]=1;Add[x]=0;
}
void down(int x,int y) {
    (sum[x][y]*=mul[x][y])+=len[x][y]*add[x][y];
    fo(i,l[x][y],r[x][y]) (p[i]*=mul[x][y])+=add[x][y];
    mul[x][y]=1;add[x][y]=0;
}
void Updata(int x) {
    Sum[x]=0;
    fo(i,1,sz[x]) Sum[x]+=sum[x][i]*mul[x][i]+len[x][i]*add[x][i];
}
void updata(int x,int y) {
    sum[x][y]=0;
    fo(i,l[x][y],r[x][y]) sum[x][y]+=p[i];
}
void change(int v,int x,int y) {
    fo(i,1,sz[v]) 
        if (x<=b[l[v][i]].v&&b[r[v][i]].v<=y) 
            mul[v][i]*=Mult,(add[v][i]*=Mult)+=Plus;
        else if (max(x,b[l[v][i]].v)<=min(y,b[r[v][i]].v)) {
            down(v,i);
            fo(j,l[v][i],r[v][i]) if (x<=b[j].v&b[j].v<=y) (p[j]*=Mult)+=Plus;
            updata(v,i);
        }
    Updata(v);
}
int query(int v,int x,int y) {
    int ans=0;
    fo(i,1,sz[v]) 
        if (x<=b[l[v][i]].v&&b[r[v][i]].v<=y) 
            ans+=sum[v][i]*mul[v][i]+len[v][i]*add[v][i];
        else if (max(x,b[l[v][i]].v)<=min(y,b[r[v][i]].v)){
            down(v,i);
            fo(j,l[v][i],r[v][i]) if (x<=b[j].v&&b[j].v<=y) ans+=p[j];
        }
    return ans;
}
int main() {
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    n=get();q=get();
    fo(i,1,n) b[i].v=get(),b[i].w=i;
    m=n/M+((n%M)>0);
    fo(i,1,m) {
        L[i]=R[i-1]+1;R[i]=min(L[i]+M,n);Len[i]=R[i]-L[i]+1;
        sort(b+L[i],b+R[i]+1,cmp);make(i);Mul[i]=1;
        fo(j,L[i],R[i]) pos[j]=i;
    }
    fo(i,1,n) a[b[i].w].w=i;
    for(;q;q--) {
        op=get();x=get();y=get();ans=0;
        if (op<=1) Mult=get(),Plus=get();
        if (!op) {
            u=pos[x];v=pos[y];
            if (u==v) {
                Down(u);fo(i,1,sz[u]) down(u,i);
                fo(i,x,y) (p[a[i].w]*=Mult)+=Plus;
                fo(i,1,sz[u]) updata(u,i);Updata(u);
                continue;
            }
            Down(u);fo(i,1,sz[u]) down(u,i);
            fo(i,x,R[u]) (p[a[i].w]*=Mult)+=Plus;
            fo(i,1,sz[u]) updata(u,i);Updata(u);
            Down(v);fo(i,1,sz[v]) down(v,i);
            fo(i,L[v],y) (p[a[i].w]*=Mult)+=Plus;
            fo(i,1,sz[v]) updata(v,i);Updata(v);
            fo(i,u+1,v-1) Mul[i]*=Mult,(Add[i]*=Mult)+=Plus;
        } else if (op==1) fo(i,1,m) Down(i),change(i,x,y);
        else if (op==2) {
            u=pos[x];v=pos[y];
            if (u==v) {
                Down(u);fo(i,1,sz[u]) down(u,i);
                fo(i,x,y) ans+=p[a[i].w];
                printf("%d\n",(ans%mo+mo)%mo);
                continue;
            }
            Down(u);fo(i,1,sz[u]) down(u,i);
            fo(i,x,R[u]) ans+=p[a[i].w];
            Down(v);fo(i,1,sz[v]) down(v,i);
            fo(i,L[v],y) ans+=p[a[i].w];
            fo(i,u+1,v-1) ans+=Sum[i]*Mul[i]+Len[i]*Add[i];
        } else fo(i,1,m) Down(i),ans+=query(i,x,y);
        if (op>=2) printf("%d\n",(ans%mo+mo)%mo);
    }
}

Ps:这是一个超时代码,其余优化留给读者自行思考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值