2024.04.04 分块补题
2024/4/4 分块补题
P3203 [HNOI2010] 弹飞绵羊
分块跳跳跳,核心是每次跳出当前块,用 \(to[i]\) 表示跳到的位置。
#include <bits/stdc++.h>
using namespace std;
#define ld long double
template <typename T>
inline T read(){
T x=0;char ch=getchar();bool fl=false;
while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return fl?-x:x;
}
#define read() read<int>()
const int maxn = 2e5 + 10;
int be[maxn],L[maxn],R[maxn],cnt,B;
int n,m,a[maxn];
int to[maxn],f[maxn];
inline int query(int x){
int res=0;
while(x<=n){
res+=f[x];x=to[x];
}
return res;
}
int main(){
n=read();B=sqrt(n);
for(int i=1;i<=n;i++){
a[i]=read();
be[i]=(i-1)/B+1;
if(!L[be[i]])L[be[i]]=i,cnt++;
R[be[i]]=i;
}
for(int i=n;i>=1;i--){
if(i+a[i]>R[be[i]]){
to[i]=i+a[i];
f[i]=1;
}
else to[i]=to[i+a[i]],f[i]=f[i+a[i]]+1;
}
m=read();
while(m--){
int op=read(),pos=read()+1;
if(op==1)printf("%d\n",query(pos));
else {
int x=read();a[pos]=x;
for(int i=pos;i>=L[be[pos]];i--){
if(i+a[i]>R[be[i]]){
to[i]=i+a[i];
f[i]=1;
}
else to[i]=to[i+a[i]],f[i]=f[i+a[i]]+1;
}
}
}
return 0;
}
P4168 [Violet] 蒲公英
\(m\) 次询问,每次询问区间 \([L,R]\) 的众数,强制在线。
处理出整块 \(f_{i,j}\) 的众数是谁。
对于边角块出现的数,强行计数,用前缀和来处理整块的该数出现的次数。
#include <bits/stdc++.h>
using namespace std;
#define ld long double
template <typename T>
inline T read(){
T x=0;char ch=getchar();bool fl=false;
while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return fl?-x:x;
}
#define read() read<int>()
const int maxn = 40000 + 10;
const int maxm = 200 + 10;
int be[maxn],L[maxn],R[maxn],B,tot,cnt[maxn];
int n,m,a[maxn],tmp[maxn],mp[maxn];
int mx[maxm][maxm],f[maxm][maxn];
inline int query(int l,int r){
if(be[l]==be[r]){
for(int i=l;i<=r;i++)cnt[a[i]]=0;
for(int i=l;i<=r;i++)cnt[a[i]]++;
int maxx=0,res=0;
for(int i=l;i<=r;i++){
if(maxx<cnt[a[i]])maxx=cnt[a[i]],res=a[i];
else if(maxx==cnt[a[i]])res=min(res,a[i]);
}
return mp[res];
}
int res1=mx[be[l]+1][be[r]-1],maxx1=f[be[r]-1][res1]-f[be[l]][res1];
int res2=0,maxx2=0;
for(int i=l;i<=R[be[l]];i++)cnt[a[i]]=0;
for(int i=L[be[r]];i<=r;i++)cnt[a[i]]=0;
for(int i=l;i<=R[be[l]];i++){
cnt[a[i]]++;
if(a[i]==res1)maxx1++;
int temp=cnt[a[i]]+f[be[r]-1][a[i]]-f[be[l]][a[i]];
if(maxx2<temp)maxx2=temp,res2=a[i];
else if(maxx2==temp)res2=min(res2,a[i]);
}
for(int i=L[be[r]];i<=r;i++){
cnt[a[i]]++;
if(a[i]==res1)maxx1++;
int temp=cnt[a[i]]+f[be[r]-1][a[i]]-f[be[l]][a[i]];
if(maxx2<temp)maxx2=temp,res2=a[i];
else if(maxx2==temp)res2=min(res2,a[i]);
}
//cerr<<maxx1<<" "<<maxx2<<endl;//
//cerr<<res1<<" "<<res2<<endl;//
if(maxx1>maxx2)return mp[res1];
else if(maxx1==maxx2)return min(mp[res1],mp[res2]);
else return mp[res2];
}
int main(){
n=read();m=read();B=sqrt(n);
for(int i=1;i<=n;i++)a[i]=read(),tmp[i]=a[i];
sort(tmp+1,tmp+1+n);
tot=unique(tmp+1,tmp+1+n)-(tmp+1);
for(int i=1;i<=n;i++){
be[i]=(i-1)/B+1;
if(!L[be[i]])L[be[i]]=i;
R[be[i]]=i;
int x=lower_bound(tmp+1,tmp+1+tot,a[i])-tmp;
mp[x]=a[i];a[i]=x;
}
for(int i=1;i<=be[n];i++){
for(int j=L[i];j<=R[i];j++)f[i][a[j]]++;
for(int j=1;j<=tot;j++)f[i][j]+=f[i-1][j];
}
for(int i=1;i<=be[n];i++){
memset(cnt,0,sizeof cnt);
int maxx=0,res=0;
for(int j=i;j<=be[n];j++){
for(int k=L[j];k<=R[j];k++){
cnt[a[k]]++;
if(cnt[a[k]]>maxx)maxx=cnt[a[k]],res=a[k];
else if(cnt[a[k]]==maxx)res=min(res,a[k]);
}
mx[i][j]=res;
}
}
int x=0;
while(m--){
int l0=read(),r0=read();
int l=(l0+x-1)%n+1,r=(r0+x-1)%n+1;
if(l>r)swap(l,r);
x=query(l,r);
printf("%d\n",x);
}
return 0;
}
ICPC2023 Macau C.Bladestorm
找出贪心策略就好做了,难点在于维护连边。
可以分块打 \(tag\) 维护整个块的连边情况,\(tag\) 为 \(-1\) 表示 \(i\) 向 \(i+k\) 连边,否则向 \(tag\) 连边,及时下放标记可以保证复杂度为 \(\text{O}(n\sqrt{n})\)
#include <bits/stdc++.h>
using namespace std;
#define ld long double
template <typename T>
inline T read(){
T x=0;char ch=getchar();bool fl=false;
while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return fl?-x:x;
}
#define read() read<int>()
const int maxn = 1e5 + 10;
int be[maxn],L[maxn],R[maxn],B,mx;
int to[maxn],nxt[maxn],f[maxn],tag[maxn],lstf[maxn];
int n,k,a[maxn];
inline void change(int x,int val){
if(val>0)nxt[x]=val;
else nxt[x]=x+k;
}
inline void rebuild(int id){
for(int i=R[id];i>=L[id];i--){
if(nxt[i]>R[id])to[i]=nxt[i],f[i]=1,lstf[i]=(i>=mx)?0:1;
else to[i]=to[nxt[i]],f[i]=f[nxt[i]]+1,lstf[i]=(i>=mx)?0:lstf[nxt[i]]+1;
}
}
inline void pushdown(int id){
if(!tag[id])return ;
for(int i=L[id];i<=R[id];i++)change(i,tag[id]);
tag[id]=0;
}
inline void update(int x,int y,int val){
if(be[x]==be[y]){
pushdown(be[x]);
for(int i=x;i<=y;i++)change(i,val);
rebuild(be[x]);
return ;
}
pushdown(be[x]);
for(int i=x;i<=R[be[x]];i++)change(i,val);
rebuild(be[x]);
pushdown(be[y]);
for(int i=L[be[y]];i<=y;i++)change(i,val);
rebuild(be[y]);
for(int i=be[x]+1;i<be[y];i++)tag[i]=val;
}
inline int query(int mx){
int now=0,res=0;
while(now<mx){
if(tag[be[now]]>0)res++,now=tag[be[now]];
else if(tag[be[now]]==-1)res++,now+=k;
else {
if(be[now]!=be[mx])res+=f[now],now=to[now];
else res+=lstf[now],now=to[now];
}
}
return res;
}
int main(){
int T=read();
while(T--){
n=read();k=read();B=sqrt(n);mx=0;
for(int i=1;i<=n;i++)a[i]=read(),L[i]=R[i]=0;
for(int i=0;i<=n;i++){
be[i]=(i-1)/B+1;
if(!L[be[i]])L[be[i]]=i;
R[be[i]]=i;
nxt[i]=to[i]=n+1;
f[i]=lstf[i]=tag[i]=0;
}
be[0]=0;L[0]=R[0]=0;
set<int> S;S.insert(0);
for(int i=1;i<=n;i++){
mx=max(mx,a[i]);
S.insert(a[i]);
int lst=*(--S.lower_bound(a[i]));
int l=max(lst,a[i]-k);
update(l,a[i]-1,-1);
if(lst<a[i]-k)update(lst,a[i]-k-1,a[i]);
int ans=query(mx);
printf("%d ",ans);
}
puts("");
}
return 0;
}