(noi.ac) #51 array题解
2020-2-13 xiaoh
题意
有一个长度为(n)的序列a((1leq nleq 5 imes 10^7)),初始情况下(a_i)的值为(i),接下来有(m)个操作((1leq mleq 5 imes 10^5)),每个操作可能为以下两个之一:
(A):对于所有的(iin [1,n]),将(a_i)改为(p imes i+q) ((|p|leq 10^3,|q|leq 10^9));
(B):将(a_x)改为(y) ((|y|leq 10^9)).
要求在每一次操作结束后输出 (Sigma a).
题解
由于(n)达到(10^7)级别,因此不可能暴力维护整个(a)序列。注意到修改为区间赋值和单点修改,所以考虑用数据结构维护"单点修改"修改过的元素。这样一来(A)操作就是清空整个数据结构并重新赋初值,(B)操作就是在数据结构上添加元素/修改,并将其计入总的(sum)中。所以我们的数据结构要实现动态插入,查询某个值是否存在,以及清空,且每个操作必须在 (log)级别以内的时间内完成。不难想到平衡树可以完成上述操作(代码中平衡树的实现为fhq-Treap,即非旋Treap)。时间复杂度O(mlogn),其中查询和添加为 (log)级别的,清空(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
",sum);
}
}
后记
一开始读这道题读错了,把(A)操作硬生生读成了(a_i=p imes a_i+q),然后看了看数据差点自爆,这个八成要高精,还要写pushdown
,又懒得写高精,就直接套了long double
……后来发现题读错的时候我已经在调样例了(QAQ)没办法,只能流着泪狂删代码+修改,结果因为long double
常数太大居然又爆了一次,最终把long double
全部换成long long
之后才过……真是个悲伤的故事。