P4117 [Ynoi2018] 五彩斑斓的世界 题解
题目描述
给定长为 \(n\) 的序列 \(a\) , \(m\) 次操作:
1 l r x:将区间 \([l,r]\) 中 \(\gt x\) 的数减去 \(x\) 。2 l r x:查询区间 \([l,r]\) 中 \(x\) 的出现次数。
数据范围
- \(1\le n\le 10^6,1\le m\le 5\cdot 10^5,1\le l\le r\le n,0\le a_i,x\le 10^5\) 。
时间限制 \(\texttt{7.5s}\) ,空间限制 \(\texttt{64MB}\) 。
分析
不卡空间弱化版: \(\texttt{CF896E}\) 。
容易发现不同下标互相独立,这启示我们用分块维护。
将序列分成 \(\sqrt n\) 块,每一块需要在 \(\mathcal O(m)\) 的时间内支持以下操作:
- 给全局 \(\gt x\) 的数减去 \(x\) 。
- 还原经过若干操作后的序列,从而遇到散块操作直接重构。
- 求区间 \([l,r]\) 中 \(x\) 的出现次数。
容易发现如果不考虑散块操作,两个数在某一时刻相同,意味着此后永远相同,可以用并查集维护每个值的出现下标。
对于修改操作,记操作前序列最大值为 \(v\) 。
- 如果 \(v\ge 2\cdot x\) ,将 \([1,x]\) 合并到 \([x+1,2x]\) 上,打全局减 \(x\) 标记。
- 如果 \(x\le v\lt 2\cdot x\) ,将 \([x+1,v]\) 合并到 \([1,v-x]\) 上。
于是合并次数等于 \(v\) 的减小量,所以复杂度是对的。
散块重构直接暴力 find 求出每个点的权值即可,注意不要把重构复杂度写成 \(\mathcal O(v)\) ,并且需要狠狠地卡散块的常。
具体的,维护 vis 数组表示每个数是否出现(这些位置满足并查集的根为自己),重构时先求出所有点的真实值,再单独修改散块内的位置即可。
对于询问操作,我们需要在并查集中维护每个连通块的 sz 。
本题的并查集询问操作很特殊,每次查询所有点所在的连通块。
因此可以分析到均摊\(\mathcal O(1)\),因为每条边只会被路径压缩一次。
时间复杂度 \(\mathcal O(n+(m+v)\sqrt n)\) 。
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=1e6+5,B=1000;
int m,n;
int a[maxn],f[maxn],sz[maxn],res[maxn];
bitset<maxn> vis;
struct oper
{
int op,l,r,x;
}q[maxn];
inline int read()
{
int q=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) q=10*q+ch-'0',ch=getchar();
return q;
}
int find(int x)
{
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
void merge(int u,int v)
{
if(!vis[u]) return ;
if(!vis[v]) f[v]=v,sz[v]=0,vis[v]=1;
f[u]=v,sz[v]+=sz[u],vis[u]=0;
}
inline void work(int s,int t)
{
vis.reset();
for(int i=s;i<=t;i++) f[a[i]]=a[i],sz[a[i]]=0,vis[a[i]]=1;
for(int i=s;i<=t;i++) sz[a[i]]++;
int v=*max_element(a+s,a+t+1);
for(int i=1,tag=0;i<=m;i++)
{
int op=q[i].op,l=q[i].l,r=q[i].r,x=q[i].x;
if(l>t||r<s||v<x) continue;
if(op==1)
{
if(l<=s&&t<=r)
{
if(v>=2*x)
{
for(int j=1;j<=x;j++) merge(tag+j,tag+x+j);
tag+=x,v-=x;
}
else
{
for(int j=1;j<=v-x;j++) merge(tag+x+j,tag+j);
v=x;
}
}
else
{
for(int j=s;j<=t;j++) a[j]=find(a[j]);
for(int j=max(s,l),tmp=min(t,r);j<=tmp;j++)
{
int &u=a[j];
if(u-tag<=x) continue;
vis[u]=--sz[u];
if(!vis[u-=x]) f[u]=u,sz[u]=0,vis[u]=1;
sz[u]++;
}
}
}
else
{
if(l<=s&&t<=r) res[i]+=vis[tag+x]?sz[find(tag+x)]:0;
else for(int j=max(s,l),tmp=min(t,r);j<=tmp;j++) res[i]+=find(a[j])-tag==x;
}
}
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=m;i++) q[i].op=read(),q[i].l=read(),q[i].r=read(),q[i].x=read();
for(int s=1,t=0;s<=n;s=t+1) work(s,t=min(s+B-1,n));
for(int i=1;i<=m;i++) if(q[i].op==2) printf("%d\n",res[i]);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17557219.html
浙公网安备 33010602011771号