[DS记录]P6019 [Ynoi2010] Brodal queue
command_block
2021-07-05 23:24:18
**题意** : 维护一个长为 $n$ 的序列 $A$ ,支持:
- 区间覆盖。
- 给出 $(l,r)$ ,查询有多少二元组 $(i,j)$ 满足 $l\leq i<j\leq r$ 且 $A_i=A_j$。
强制在线,$n,m\leq 10^5$ ,时限$\texttt{3s}$ ,空限$\texttt{1G}$。
------------
> 感谢 @mrsrz 的指导。
记 $c_i$ 为 $i$ 的出现次数,则贡献为 $\binom{c_i}{2}=\frac{c_i^2-c_i}{2}$ 。于是只需计算区间内每种数出现次数的平方和。
## 询问
对于纯色的块,当做散块元素进行特殊处理。
把贡献分为三类 : “散块+纯块内部”,“散块+纯块 与 非纯块之间”,“非纯块 内部”。
散块+纯块的颜色种类数是 $O(\sqrt{n})$ 的。
- 散块+纯块 内部
容易用桶暴力计算。
- 散块+纯块 与 非纯块之间
记 $o_{i,j}=$ 前 $i$ 个(只考虑非纯块贡献)块内 $j$ 出现的次数。
记整块区间为 $[L,R]$。
对于元素 $j$ ,在散块+纯块中出现了 $c_j$ 次,则贡献为 $c_j(o_{R,j}-o_{L-1,j})$。
- 整块内部
记 $f(i,j)=\sum_xo_{i,x}o_{j,x}$ 。
块 $L\sim R$ 之中的贡献为 $f(R,R)-2f(L-1,R)+f(L-1,L-1)$ 。
## 修改
考虑如何维护 $o,f$。
对于散块,暴力重构。
对于整块,若不是纯色块,暴力重构,若是,则简单打标记。
不难发现,每次对块的暴力重构都会导致分界线减少至少 $1$ 个,每次修改只会增加 $O(1)$ 条分界线,于是总复杂度为 $O((n+m)\sqrt{n})$。
将一次修改描述为“将颜色 $x$ 在块 $k$ 中的出现次数增加了 $c$”。
这样的修改的总次数是 $O(n+m)$ 的。
每次在 $o$ 上重新做一次前缀和,复杂度为 $O\big((n+m)\sqrt{n}\big)$。
对于 $f$ ,考虑 $f(i,j)$ 。
- $i,j<k$ :变化量为 $0$ 。
- $i<k\leq j$ :变化量为 $o_{i,x}(o_{j,x}+c)-o_{i,x}o_{j,x}=o_{i,x}c$
- $k\leq i,j$ :变化量为 $(o_{i,x}+c)(o_{j,x}+c)-o_{i,x}o_{j,x}=c^2+o_{i,x}c+o_{j,x}c$
对 $f(i,j)$ 的增量维护两个方向的差分。对于 $+o_{i,x}c$ ,用 $j$ 维度的划分处理,对于 $+o_{j,x}c$ 用 $i$ 维度的差分处理。
修改复杂度容易做到 $O(\sqrt{n})$。单点查询时只需将两个方向的贡献更新,也是 $O(\sqrt{n})$。
综上,时空复杂度 $O\big((n+m)\sqrt{n}\big)$。
引用题面的一句话(大雾) :
> 实现真的很复杂,常数真的很大。
有点卡常,把数组维度交换一下提升空间连续性,很快啊,啪的一声就过了,最慢点 $\texttt{2.03s}$。
```cpp
#include<algorithm>
#include<cstdio>
#include<cmath>
#define uint unsigned int
#define ll long long
#define MaxN 200500
using namespace std;
int Bcnt;
ll f[392][392],s1[392][392],s2[392][392];
int o[MaxN][392];
void ladd1(int k){
ll now=0;
for (int i=k;i<=Bcnt;i++){
now+=s1[k][i];s1[k][i]=0;
f[k][i]+=now;
}
}
void ladd2(int k){
ll now=0;
for (int i=0;i<=k;i++){
now+=s2[k][i];s2[k][i]=0;
f[i][k]+=now;
}
}
void add(int k,int x,int c)
{
for (int i=0;i<k;i++)s1[i][k]+=o[x][i]*c;
for (int i=k;i<=Bcnt;i++){
s1[i][i]+=o[x][i]*c;
s2[i][k]+=(o[x][i]+c)*c;
}for (int i=k;i<=Bcnt;i++)o[x][i]+=c;
}
ll qry2(int l,int r){
if (l>r)return 0;
ladd1(l-1);ladd1(r);ladd2(l-1);ladd2(r);
return f[r][r]-2*f[l-1][r]+f[l-1][l-1];
}
int BS,e[MaxN],a[MaxN],tg[405];
void cov2(int l,int r,int x)
{
int k=l/BS,bl=k*BS,br=bl+BS;
if (tg[k]){
if (tg[k]==x)return ;
for (int i=bl;i<br;i++)a[i]=tg[k];
for (int i=l;i<=r;i++)a[i]=x;
add(k,x,r-l+1);add(k,tg[k],BS-(r-l+1));
tg[k]=0;
}else {
for (int i=l;i<=r;i++)e[a[i]]++;
for (int i=l;i<=r;i++){
if (e[a[i]]){add(k,a[i],-e[a[i]]);e[a[i]]=0;}
a[i]=x;
}add(k,x,r-l+1);
}
}
void del(int k){
int bl=k*BS,br=bl+BS;
for (int i=bl;i<br;i++)e[a[i]]++;
for (int i=bl;i<br;i++)
if (e[a[i]]){add(k,a[i],-e[a[i]]);e[a[i]]=0;}
}
void cov(int l,int r,int x)
{
if (l/BS==r/BS){cov2(l,r,x);return ;}
int tl=l/BS+1,tr=r/BS-1;
cov2(l,tl*BS-1,x);cov2(tr*BS+BS,r,x);
for (int i=tl;i<=tr;i++){
if (!tg[i])del(i);
tg[i]=x;
}
}
int st[MaxN];
void chk(int k){
if (!tg[k])return ;
int bl=k*BS,br=bl+BS;
for (int i=bl;i<br;i++)a[i]=tg[k];
}
ll qry(int l,int r)
{
int tn=0;ll ret=0;
if (l/BS==r/BS){
chk(l/BS);
for (int i=l;i<=r;i++)if (!e[a[i]]++)st[++tn]=a[i];
for (int i=1;i<=tn;i++)
{ret+=e[st[i]]*e[st[i]];e[st[i]]=0;}
return ret;
}chk(l/BS);chk(r/BS);
int tl=l/BS+1,tr=r/BS-1;
for (int i=l;i<tl*BS;i++)if (!e[a[i]]++)st[++tn]=a[i];
for (int i=tr*BS+BS;i<=r;i++)if (!e[a[i]]++)st[++tn]=a[i];
for (int i=tl;i<=tr;i++)if (tg[i]){
if (!e[tg[i]])st[++tn]=tg[i];
e[tg[i]]+=BS;
}
for (int i=1;i<=tn;i++){
ret+=1ll*e[st[i]]*(e[st[i]]+2*(o[st[i]][tr]-o[st[i]][tl-1]));
e[st[i]]=0;
}return ret+qry2(tl,tr);
}
int n,m;
int main()
{
scanf("%d%d",&n,&m);
BS=sqrt(n*1.33)+1;Bcnt=n/BS;
for (int i=1;i<=n;i++)scanf("%d",&a[i]);
a[0]=1;for (int i=n+1;i<Bcnt*BS+BS;i++)a[i]=1;
for (int k=0;k<=Bcnt;k++){
int bl=k*BS,br=bl+BS;
for (int i=bl;i<br;i++)
for (int j=k;j<=Bcnt;j++)
o[a[i]][j]++;
}
for (int k=0;k<=Bcnt;k++){
int bl=k*BS,br=bl+BS;
for (int k2=k;k2<=Bcnt;k2++){
if (k>0)f[k][k2]=f[k-1][k2];
for (int i=bl;i<br;i++)
f[k][k2]+=o[a[i]][k2];
}
}
uint l,r,x,las=0;
for (int i=1,op;i<=m;i++){
scanf("%d%u%u",&op,&l,&r);
l^=las;r^=las;
if (op==1){
scanf("%u",&x);x^=las;
cov(l,r,x);
}else {
ll ans=(qry(l,r)-(r-l+1))/2;
las=ans;
printf("%lld\n",ans);
}
}return 0;
}
```