题解:P2801 教主的魔法
看题解区没有讲单根号的做法的,我来发一个。直接冲到了最优解第二,仅次于神秘
首先确保你能理解二分做法,随后考虑怎么消掉这只
我们枚举每一个块,算这个块对查询操作的贡献。正常二分做法是二分出一个最大的
分析复杂度,共有
不知道为什么,我的最优块长达到了惊人的
代码略丑,谨慎观看。
// Problem: P2801 教主的魔法
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2801
// Memory Limit: 128 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
bool startmb;
constexpr int N=1<<20;
int n,q,ans[N];
struct node{int id,x;node(int _id,int _x){id=_id,x=_x;}node(){}}a[N];
struct Node{int id;char op;int l,r,x;}x[N];
int cyc[N];
vector<Node> vec[N];
//封装的基数排序
int cnt[257];
node bt[1<<20];
template<typename T>
void mysort(T startpoint,T endpoint)
{
for(int i=0;i<=255;i++) cnt[i]=0;
for(auto i=startpoint;i!=endpoint;i++) cnt[(*i).x&255]++;
for(int i=0;i<=255;i++) cnt[i+1]+=cnt[i];
for(int i=255;i>=1;i--) cnt[i]=cnt[i-1];
cnt[0]=0;
for(auto i=startpoint;i!=endpoint;i++) bt[cnt[(*i).x&255]++]=*i;
for(auto i=startpoint;i!=endpoint;i++) *i=bt[i-startpoint];
for(int i=0;i<=255;i++) cnt[i]=0;
for(auto i=startpoint;i!=endpoint;i++) cnt[(*i).x>>8&255]++;
for(int i=0;i<=255;i++) cnt[i+1]+=cnt[i];
for(int i=255;i>=1;i--) cnt[i]=cnt[i-1];
cnt[0]=0;
for(auto i=startpoint;i!=endpoint;i++) bt[cnt[(*i).x>>8&255]++]=*i;
for(auto i=startpoint;i!=endpoint;i++) *i=bt[i-startpoint];
for(int i=0;i<=255;i++) cnt[i]=0;
for(auto i=startpoint;i!=endpoint;i++) cnt[(*i).x>>16&255]++;
for(int i=0;i<=255;i++) cnt[i+1]+=cnt[i];
for(int i=255;i>=1;i--) cnt[i]=cnt[i-1];
cnt[0]=0;
for(auto i=startpoint;i!=endpoint;i++) bt[cnt[(*i).x>>16&255]++]=*i;
for(auto i=startpoint;i!=endpoint;i++) *i=bt[i-startpoint];
for(int i=0;i<=255;i++) cnt[i]=0;
for(auto i=startpoint;i!=endpoint;i++) cnt[(*i).x>>24&255]++;
for(int i=0;i<=255;i++) cnt[i+1]+=cnt[i];
for(int i=255;i>=1;i--) cnt[i]=cnt[i-1];
cnt[0]=0;
for(auto i=startpoint;i!=endpoint;i++) bt[cnt[(*i).x>>24&255]++]=*i;
for(auto i=startpoint;i!=endpoint;i++) *i=bt[i-startpoint];
}
constexpr int B=32768;
int ll[N/B],rl[N/B];//块的左右端点
//快读
char but[1<<22],*p1=but,*p2=but;
#define getchar() (p1==p2&&(p2=(p1=but)+fread(but,1,1<<22,stdin),p1==p2)?EOF:*p1++)
void read(int&x)
{
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
}
void read(char&x)
{
x=getchar();
while(!isupper(x)) x=getchar();
}
bool endmb;
main()
{
cerr<<((&startmb-&endmb)/1024.0/1024)<<endl;
cin.tie(0)->sync_with_stdio(false);
read(n),read(q);
for(int i=0;i<n;i++) read(a[i].x),a[i].id=i;
for(int i=0;i<n;i++) rl[i/B]=i;
for(int i=n-1;i>=0;i--) ll[i/B]=i;
for(int i=0;i<q;i++) read(x[i].op),read(x[i].l),read(x[i].r),read(x[i].x),x[i].id=i,x[i].l--,x[i].r--;
for(int i=0;i<q;i++) for(int k=x[i].l/B;k<=x[i].r/B;k++) cyc[k]++;
for(int i=0;i<=n/B;i++) vec[i].reserve(cyc[i]);
for(int i=0;i<q;i++)//把操作下放到块上
{
if(x[i].l/B==x[i].r/B) vec[x[i].l/B].emplace_back(x[i]);
else
{
vec[x[i].l/B].emplace_back(Node{i,x[i].op,x[i].l,rl[x[i].l/B],x[i].x});
vec[x[i].r/B].emplace_back(Node{i,x[i].op,ll[x[i].r/B],x[i].r,x[i].x});
for(int k=x[i].l/B+1;k<x[i].r/B;k++) vec[k].emplace_back(Node{i,x[i].op,ll[k],rl[k],x[i].x});
}
}
for(int k=0;k<=n/B;k++)//枚举每一块
{
int tag=0;
mysort(a+ll[k],a+rl[k]+1);//排序
vector<node> que;
for(auto i=vec[k].begin();i!=vec[k].end();i++)
{
if((*i).op=='M')
{
if((*i).l==ll[k]&&(*i).r==rl[k]) {tag+=(*i).x;continue;}//整块修改直接打tag
if(que.size())//处理整块查询
{
mysort(que.begin(),que.end());//排序
int point=ll[k];
for(auto [id,x]:que)
{
while(point<=rl[k]&&a[point].x<x) point++;//指针
ans[id]+=rl[k]-point+1;
}
que.clear();
}
//散块修改用归并,保证有序的同时保证复杂度
vector<node> xl,xr;
xl.reserve(B),xr.reserve(B);
for(int x=ll[k];x<=rl[k];x++) ((*i).l<=a[x].id&&a[x].id<=(*i).r?xr:xl).emplace_back(a[x]);
for(auto&[id,x]:xr) x+=(*i).x;
auto idl=xl.begin(),idr=xr.begin();
auto idp=a+ll[k];
while(idl!=xl.end()&&idr!=xr.end()) *idp++=*((*idl).x<=(*idr).x?idl:idr)++;
while(idl!=xl.end()) *idp++=*idl++;
while(idr!=xr.end()) *idp++=*idr++;
}
else
{
(*i).x=max(0,(*i).x-tag);//算C-tag
if((*i).l==ll[k]&&(*i).r==rl[k]) que.emplace_back((*i).id,(*i).x);//整块查询后面再查
else for(int x=ll[k];x<=rl[k];x++) if((*i).l<=a[x].id&&a[x].id<=(*i).r&&a[x].x>=(*i).x) ans[(*i).id]++;//散块查询直接查
}
}
//最后把没查过的查完
if(que.size())
{
mysort(que.begin(),que.end());
int point=ll[k];
for(auto [id,x]:que)
{
while(point<=rl[k]&&a[point].x<x) point++;
ans[id]+=rl[k]-point+1;
}
que.clear();
}
}
for(int i=0;i<q;i++) if(x[i].op=='A') cout<<ans[i]<<'\n';
return 0;
}
最后感谢 zcz0263 大佬给出的这个做法,还帮我调题。orz