P4117 Ynoi2018 五彩斑斓的世界
P4117 Ynoi2018 五彩斑斓的世界
听说 gal 圈神作,有空推。
思路
最近在做值域分块,形式和这玩意很像。但发现,没有好的数据结构维护相同的值位置,且值域没有想象中的大。
考虑正常的序列分块。对于 \(mi>x\) 的块打上整体减的 \(tag\),否则暴力处理该块的情况,用并查集维护块内相同的数的位置和个数。
利用上值域的性质,发现一个块减去的和小于 \(V(\leq 10^5)\)。我们枚举出需要修改的并查集,并且让我们的枚举量的总和为 \(V\) 就能以 \(O(V\sqrt n)\) 的时间通过本题。
设块内最大值为 \(mx\)。
若 \(mx\leq 2\cdot x\),遍历大于 \(x\) 的并查集并进行修改,时间复杂度 \(O(mx-x)\),最大值至少下降 \(mx-x\)。
若 \(mx>2\cdot x\),遍历小于 \(x\) 的并查集并进行修改,时间复杂度 \(O(x)\),最大值至少下降 \(x\)。
最大值下降程度与时间复杂度同级,故单块时间复杂度为 \(O(n)\)。
同时你仔细思考后会发现,如果不按照上面的偏序关系遍历,总是会花费更多的时间遍历,但最大值下降程度却比复杂度低。
到这里题解就要结束了……吗?
看到空间限制 64mb……
我们正常的分块是每个块开一个并查集,花销为 \(O(V\sqrt n+n)\),\(V\) 是每一个块内,记录每个值对应的并查集根部的数组。
注意到,现在的做法是可以强制在线的……
实际上,本体中每一个块是极为独立的,任意一个块的操作都无法对其他块产生影响。
启发我们把每一个块拆下来,这 \(O(\sqrt n)\) 个块都依次做 \(m\) 个操作,这样就可以共用一个权值数组,数组大小缩小至 \(O(V+n)\)。
到这里题解就要结束了……吗?
你会发现有点卡常,在快读、inline 等基础卡常下给出一下建议。
- 少一切无必要或可合并的循环,比如统计散块答案不用遍历整个区间。
- 使用
C++98 O2提交。 - 调整合适的块长,我是 \(\sqrt n\) 块长过的。
CODE
// #pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5,maxm=5e5+5,maxV=1e5+5,smaxn=1e3+5;
int n,m,B;
int a[maxn],ans[maxm];
struct worknode{int op,l,r,x;}chg[maxm];
int l,r,tag,mx;
inline int read()
{
int sum=0,f=1;
char c=getchar();
while(c!='-'&&(c<'0'||c>'9')) c=getchar();
if(c=='-') f=-1,c=getchar();
while('0'<=c&&c<='9') sum=(sum<<1)+(sum<<3)+c-'0',c=getchar();
return sum*f;
}
inline void write(int x) {
static int sta[35];
int top = 0;
do {
sta[top++] = x % 10;
x /= 10;
} while (x);
while (top) putchar(sta[--top] + '0');
}
//DSU
int rt[maxV],fa[maxn],sz[maxn],frt[maxn];
inline int fr(int u){return fa[u]==u?u:fa[u]=fr(fa[u]);}
inline void merge(int u,int v)
{
if(!rt[v]) rt[v]=rt[u],frt[rt[v]]=v;
else
{
if(sz[rt[u]]<sz[rt[v]])
fa[rt[u]]=rt[v],sz[rt[v]]+=sz[rt[u]],frt[rt[u]]=0;
else
{
fa[rt[v]]=rt[u],sz[rt[u]]+=sz[rt[v]];
frt[rt[v]]=0,frt[rt[u]]=v;
rt[v]=rt[u];
}
}
rt[u]=0;
}
inline void rebuild(bool flg)
{
mx=tag=0;
if(flg) memset(rt,0,sizeof(rt));
else for(int i=l;i<=r;i++) rt[frt[i]]=0;
for(int i=l;i<=r;i++)
{
fa[i]=sz[i]=frt[i]=0;
mx=max(a[i],mx);
if(rt[a[i]]) fa[i]=rt[a[i]],sz[fa[i]]++;
else rt[a[i]]=fa[i]=i,sz[i]=1,frt[i]=a[i];
}
}
inline void modify(int val)
{
if(mx-tag<=val) return ;
if(mx-tag>=(val<<1))
{
for(int i=1+tag;i<=val+tag;i++) if(rt[i]) merge(i,i+val);
tag+=val;
}
else
{
for(int i=mx;i>val+tag;i--) if(rt[i]) merge(i,i-val);
if(tag+val<mx) mx=tag+val;
}
}
inline void pushtag(){for(int i=l;i<=r;i++) a[i]=frt[fr(i)]-tag;}
inline int qry(int val)
{
if(val+tag>maxV-1) return 0;
return sz[rt[val+tag]];
}
int main()
{
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
n=read(),m=read();B=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=m;i++)
{
int op,l,r,x;
op=read(),l=read(),r=read(),x=read();
chg[i]={op,l,r,x};
}
for(int i=1;i<=(n-1)/B+1;i++)
{
// cerr<<i<<" "<<clock()<<"\n";
l=r+1,r=min(l+B-1,n);
rebuild(1);
for(int k=1;k<=m;k++)
{
if(chg[k].r<l||chg[k].l>r) continue;
if(chg[k].op==1)
{
if(chg[k].l<=l&&r<=chg[k].r) modify(chg[k].x);
else
{
pushtag();
for(int i=max(chg[k].l,l);i<=min(chg[k].r,r);i++) a[i]-=(a[i]>chg[k].x?chg[k].x:0);
rebuild(0);
}
}
else
{
if(chg[k].l<=l&&r<=chg[k].r) ans[k]+=qry(chg[k].x);
else
for(int i=max(chg[k].l,l);i<=min(chg[k].r,r);i++) ans[k]+=((frt[fr(i)]-tag)==chg[k].x);
}
}
}
for(int i=1;i<=m;i++) if(chg[i].op==2) write(ans[i]),putchar('\n');
}

浙公网安备 33010602011771号