noi.ac #51 array 题解

noi.acnoi.ac #51 array题解

2020-2-13 xiaoh

题意

有一个长度为nn的序列a(1n5×1071\leq n\leq 5\times 10^7),初始情况下aia_i的值为ii,接下来有mm个操作(1m5×1051\leq m\leq 5\times 10^5),每个操作可能为以下两个之一:
AA:对于所有的i[1,n]i\in [1,n],将aia_i改为p×i+qp\times i+q (p103,q109|p|\leq 10^3,|q|\leq 10^9);
BB:将axa_x改为yy (y109|y|\leq 10^9).
要求在每一次操作结束后输出 Σa\Sigma a.

题解

由于nn达到10710^7级别,因此不可能暴力维护整个aa序列。注意到修改为区间赋值和单点修改,所以考虑用数据结构维护"单点修改"修改过的元素。这样一来AA操作就是清空整个数据结构并重新赋初值,BB操作就是在数据结构上添加元素/修改,并将其计入总的sumsum中。所以我们的数据结构要实现动态插入,查询某个值是否存在,以及清空,且每个操作必须在 log\log级别以内的时间内完成。不难想到平衡树可以完成上述操作(代码中平衡树的实现为fhq-Treap,即非旋Treap)。时间复杂度O(mlogn),其中查询和添加为 log\log级别的,清空O(1)O(1)

Code

#include<bits/stdc++.h>
using namespace std;
const int MAXN=500010;
int tot=0;
int rt=0;
long long sum;
long long add=0,mul=1;
struct node{//fhq-Treap的代码部分注释就不赘述了,就是一个板子
    int l,r;
    int val,dat;
    long long val2;
    int sz;
}f[MAXN];
inline void pushup(int p)
{
    f[p].sz=f[f[p].l].sz+f[f[p].r].sz+1;
}
inline int New(int val,long long val2)
{
    tot++;
    f[tot].val=val,f[tot].val2=val2,f[tot].dat=rand();
    f[tot].sz=1;
    return tot;
}
void split(int p,int k,int &x,int &y)
{
    if(!p)
    {
        x=y=0;
        return;
    }
    if(f[p].val<=k)
    {
        x=p;
        split(f[p].r,k,f[p].r,y);
    }
    else
    {
        y=p;
        split(f[p].l,k,x,f[p].l);
    }
    pushup(p);
}
int merge(int a,int b)
{
    if(!a||!b) return a+b;
    if(f[a].dat<f[b].dat)
    {
        f[a].r=merge(f[a].r,b);
        pushup(a);
        return a;
    }
    else
    {
        f[b].l=merge(a,f[b].l);
        pushup(b);
        return b;
    }
}
inline bool find(int val)//查找值是否存在
{
    int x,y,z;
    split(rt,val-1,x,y);
    split(y,val,y,z);
    bool ret=(y!=0);
    rt=merge(merge(x,y),z);
    return ret;
}
inline void insert(int p,long long q)
{
    if(find(p))//若值存在则直接修改
    {
        int x,y,z;
        split(rt,p-1,x,y);
        split(y,p,y,z);
        sum-=f[y].val2;
        sum+=q;
        f[y].val2=q;
        rt=merge(merge(x,y),z);
    }
    else//否则插入这个值
    {
        int x,y;
        split(rt,p,x,y);
        sum-=(long long)p*mul+add;
        sum+=q;
        rt=merge(merge(x,New(p,q)),y);
    }
}
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    sum=(long long)n*(long long)(n+1)/(long long)2;
    for(register int i=1;i<=m;i++)
    {
        char op[10];
        scanf("%s",op);
        if(op[0]=='A')
        {
            long long p,q;
            scanf("%lld%lld",&p,&q);
            mul=p,add=q;
            sum=(long long)n*(long long)(n+1)/(long long)2;//修改总和
            sum=sum*p+q*(long long)n;
            rt=0;//清空fhq-Treap
        }
        else
        {
            int x;
            long long y;
            scanf("%d%lld",&x,&y);
            insert(x,y);//修改对应的值
        }
        printf("%lld\n",sum);
    }
}

后记

一开始读这道题读错了,把AA操作硬生生读成了ai=p×ai+qa_i=p\times a_i+q,然后看了看数据差点自爆,这个八成要高精,还要写pushdown,又懒得写高精,就直接套了long double……后来发现题读错的时候我已经在调样例了QAQQAQ没办法,只能流着泪狂删代码+修改,结果因为long double常数太大居然又爆了一次,最终把long double全部换成long long之后才过……真是个悲伤的故事。