【uoj#46】 [清华集训2014] 玄学

  题目传送门:uoj46

  题意简述:要求在序列上维护一个操作间支持结合律的区间操作,查询连续一段时间内的操作对单点的作用效果,\(n \leq 10^5,m \leq 6 \times 10^5\)

  刚开始看到这道题的时候想到树套树,然而需要用到的空间太大,时间复杂度无法承受,再一看有8s的时限,就试着强行对操作分块,看看能不能\(O(m \sqrt m)\)卡过去,不过极限数据下再怎么优化也要10s,卡不过去。

  正解可以用二进制分组的思想,对操作序列维护一棵线段树,每个结点维护子树内的操作对序列每个元素的影响(相邻两个元素的影响相同合并成一个区间表示)。然而不同于普通线段树的是,该线段树仅在左右儿子都塞满操作的时候才更新父节点的信息,这样每个结点只会被更新一次。在更新父节点的时候,可以归并地把左右儿子的信息叠加,可以证明这样的修改复杂度均摊是\(O(m\log m)\)的。查询时可以把查询的时间区间分割到线段树的\(\log m\)个结点上,然后在在这些结点上二分查找待查询点的操作效果。总时间复杂度\(O(m\log^2m)\),但实际上常数较小,能很快通过极限数据。

  代码:

uoj46

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define inf 0x3f3f3f3f
#define maxn 100010
#define maxm 600010
inline ll read()
{
    ll x=0; char c=getchar(),f=1;
    for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1;
    for(;'0'<=c&&c<='9';c=getchar())x=x*10+c-'0';
    return x*f;
}
inline void write(ll x)
{
    static char buf[20];
    int len=0;
    if(x<0)putchar('-'),x=-x;
    for(;x;x/=10)buf[len++]=x%10+'0';
    if(!len)putchar('0');
    else while(len)putchar(buf[--len]);
}
inline void writesp(ll x){write(x); putchar(' ');}
inline void writeln(ll x){write(x); putchar('\n');}
int a[maxn];
int T,n,m,P;
struct Opt{
    ll a,b;
    friend Opt operator * (Opt a,Opt b){return (Opt){a.a*b.a%P,(b.a*a.b+b.b)%P};}
};
const Opt one={1,0};
struct Data{
    int l,r;
    Opt k;
};
struct point{
    std::vector<Data>seq;
    int full;
}sgt[4*maxm];
void merge(std::vector<Data>&a,std::vector<Data>&b,std::vector<Data>&ret)
{
    int sza=a.size(),szb=b.size(),pa=0,pb=0;
    a.push_back((Data){n+1,n+1,one}); b.push_back((Data){n+1,n+1,one});
    ret.clear();
    while(pa<sza||pb<szb){
        int last=std::min(a[pa].l,b[pb].l);
        if(a[pa].r<b[pb].r)ret.push_back((Data){last,a[pa].r,a[pa].k*b[pb].k}),++pa;
        else ret.push_back((Data){last,b[pb].r,a[pa].k*b[pb].k}),++pb;
    }
    a.pop_back(); b.pop_back();
}
void add(int now,int l,int r,int x,int y,Opt k)
{
    // printf("%d %d %d %d %d %lld %lld\n",now,l,r,x,y,k.a,k.b);
    if(l==r){
        sgt[now].full=1;
        if(x>1)sgt[now].seq.push_back((Data){1,x-1,one});
        sgt[now].seq.push_back((Data){x,y,k});
        if(y<n)sgt[now].seq.push_back((Data){y+1,n,one});
    }
    else{
        int mid=(l+r)>>1;
        if(!sgt[now<<1].full)add(now<<1,l,mid,x,y,k);
        else add(now<<1|1,mid+1,r,x,y,k);
        sgt[now].full=sgt[now<<1].full&sgt[now<<1|1].full;
        if(sgt[now].full)merge(sgt[now<<1].seq,sgt[now<<1|1].seq,sgt[now].seq);
    }
}
Opt find(std::vector<Data>&a,int pos)
{
    int l=0,r=a.size()-1;
    while(l<r){
        int mid=(l+r)>>1;
        if(a[mid].r<pos)l=mid+1;
        else r=mid;
    }
    return a[l].k;
}
Opt query(int now,int l,int r,int x,int y,int pos)
{
    if(x<=l&&r<=y)return find(sgt[now].seq,pos);
    else{
        int mid=(l+r)>>1;
        Opt cur=one;
        if(x<=mid)cur=cur*query(now<<1,l,mid,x,y,pos);
        if(mid<y)cur=cur*query(now<<1|1,mid+1,r,x,y,pos);
        return cur;
    }
}
int main()
{
    freopen("uoj46.in","r",stdin);
    freopen("uoj46.out","w",stdout);
    T=read();
    n=read(); P=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    m=read();
    int lastans=0;
    for(int i=1;i<=m;i++){
        int op=read();
        if(op==1){
            int l=read(),r=read(),x=read(),y=read();
            if(T&1)l^=lastans,r^=lastans;
            add(1,1,m,l,r,(Opt){x,y});
        }
        else{
            int l=read(),r=read(),pos=read();
            if(T&1)l^=lastans,r^=lastans,pos^=lastans;
            Opt ans=query(1,1,m,l,r,pos);
            lastans=(ans.a*a[pos]+ans.b)%P;
            writeln(lastans);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/quzhizhou/p/11738317.html