2023
教主的魔法
大水题
注意开long long
。另开数组
查询的时候散块暴力,整块倍增就OK
#define N 1050050
#define int long long
int L[N],R[N],siz,block,t,a[N],tag[N],b[N],pos[N],n,m;
void get(int id){
if(tag[id]!=0){
for(int i=L[id];i<=R[id];i++)a[i]+=tag[id];
}
for(int i=L[id];i<=R[id];i++)b[i]=a[i];
sort(b+L[id],b+R[id]+1);
}
void init(){
cin>>n>>m;block=sqrt(n),siz=(n+block-1)/block;t=log(n)/log(2)+1;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=siz;i++){
L[i]=R[i-1]+1,R[i]=min(L[i]+block-1,n);
for(int j=L[i];j<=R[i];j++){
pos[j]=i,b[j]=a[j];
}
}
for(int i=1;i<=siz;i++)sort(b+L[i],b+R[i]+1);
}
void change(int l,int r,int k){
if(pos[l]==pos[r]){
for(int i=l;i<=r;i++)a[i]+=k;
get(pos[l]);return ;
}
for(int i=l;i<=R[pos[l]];i++)a[i]+=k;
for(int i=L[pos[r]];i<=r;i++)a[i]+=k;
for(int i=pos[l]+1;i<pos[r];i++)tag[i]+=k;
get(pos[l]);get(pos[r]);
}
int query(int l,int r,int k){
int res=0;
if(pos[l]==pos[r]){
for(int i=l;i<=r;i++)res+=(a[i]+tag[pos[l]]>=k);
return res;
}
for(int i=l;i<=R[pos[l]];i++)res+=(a[i]+tag[pos[l]]>=k);
for(int i=L[pos[r]];i<=r;i++)res+=(a[i]+tag[pos[r]]>=k);
for(int i=pos[l]+1;i<pos[r];i++){
int p=L[i]-1;
for(int j=t;j>=0;--j){
if(p+(1<<j)>R[i])continue;
if(b[p+(1<<j)]+tag[i]<k)p+=(1<<j);
}
res+=R[i]-p;
}
return res;
}
signed main(){
ios::sync_with_stdio(false);
init();
while(m--){
char opt;int l,r,k;
cin>>opt>>l>>r>>k;
if(opt=='M'){
change(l,r,k);
}
else cout<<query(l,r,k)<<"\n";
}
return 0;
}
作诗
分块超级大暴力。
预处理每个数载每个块的出现次数,求个前缀和。预处理每个块区间的答案
查询的时候小块暴力,否则先拿到中间超级大块的答案,两边散块结合前缀和分类讨论乱搞即可
具体地:
设
设
- 枚举
中的每个数,查询这个数在 的出现次数与 的出现次数,分类讨论更新答案(记得只能统计一次,可以通过vis
实现,记得撤销) - 大功告成
对于答案的查询,首先
然后将在cnt
,然后统计答案就类似于预处理的第2步,具体可以看代码
void init(){
cin>>n>>c>>m;++c;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)a[i]++;
block=sqrt(n),siz=(n+block-1)/block;
for(int i=1;i<=siz;i++)L[i]=R[i-1]+1,R[i]=min(n,L[i]+block-1);
for(int i=1;i<=siz;i++)for(int j=L[i];j<=R[i];j++)pos[j]=i;
for(int i=1;i<=siz;i++){
for(int j=1;j<=c;j++)sum[i][j]=sum[i-1][j];
for(int j=L[i];j<=R[i];j++)sum[i][a[j]]++;
}
for(int i=1;i<=siz;i++){
for(int j=L[i];j<=R[i];j++){
int k=cnt[a[j]];
if(k&1)ans[i][i]++;
else if(k>0)ans[i][i]--;
cnt[a[j]]++;
}
for(int j=L[i];j<=R[i];j++)cnt[a[j]]=0;
}
for(int i=1;i<=siz;i++){
for(int j=i+1;j<=siz;j++){
ans[i][j]=ans[i][j-1];
for(int k=L[j];k<=R[j];k++){
if(vis[a[k]])continue;
vis[a[k]]=1;
int pre=sum[j-1][a[k]]-sum[i-1][a[k]];
int now=sum[j][a[k]]-sum[j-1][a[k]];
if(!pre){
if(now>0&&now%2==0)ans[i][j]++;
}
else if(pre&1) {
if(now&1)ans[i][j]++;
}
else if(now&1)ans[i][j]--;
}
for(int k=L[j];k<=R[j];k++)vis[a[k]]=0;
}
}
}
int solve(int l,int r){
int Ans=0;
if(pos[l]==pos[r]){
for(int i=l;i<=r;i++){
int k=cnt[a[i]];
if(k&1)Ans++;
else if(k>0)Ans--;
cnt[a[i]]++;
}
for(int i=l;i<=r;i++)cnt[a[i]]=0;
return Ans;
}
Ans=ans[pos[l]+1][pos[r]-1];
for(int i=l;i<=R[pos[l]];i++)cnt[a[i]]=sum[pos[r]-1][a[i]]-sum[pos[l]][a[i]];
for(int i=L[pos[r]];i<=r;i++)cnt[a[i]]=sum[pos[r]-1][a[i]]-sum[pos[l]][a[i]];
for(int i=l;i<=R[pos[l]];i++){
int k=cnt[a[i]];
if(k&1)Ans++;
else if(k>0)Ans--;
cnt[a[i]]++;
}
for(int i=L[pos[r]];i<=r;i++){
int k=cnt[a[i]];
if(k&1)Ans++;
else if(k>0)Ans--;
cnt[a[i]]++;
}
for(int i=l;i<=R[pos[l]];i++)cnt[a[i]]=0;
for(int i=L[pos[r]];i<=r;i++)cnt[a[i]]=0;
return Ans;
}
小Z的袜子
对于一个区间,设
则:
根据二项式系数的性质:
可以将分子拆为:
这样就可以愉快地莫队啦!
记得用gcd
化简哦
void add(int x){
res+=cnt[a[x]];++cnt[a[x]];
}
void del(int x){
--cnt[a[x]];res-=cnt[a[x]];
}
void solve(){
int l=1,r=0;
for(int i=1;i<=m;i++){
while(l>ask[i].l)add(--l);
while(r<ask[i].r)add(++r);
while(l<ask[i].l)del(l++);
while(r>ask[i].r)del(r--);
int len=r-l+1;
int d=gcd(res,len*(len-1)/2);
ans1[ask[i].id]=res/d;ans2[ask[i].id]=len*(len-1)/2/d;
}
for(int i=1;i<=m;i++)cout<<ans1[i]<<"/"<<ans2[i]<<"\n";
}
HH的项链
莫队板题,没啥好说
void add(int x){
++cnt[a[x]];if(cnt[a[x]]==1)res++;
}
void del(int x){
--cnt[a[x]];if(cnt[a[x]]==0)res--;
}
蒲公英
昨天的T9,强制在线,就不能写莫队了
一样,先预处理整块区间答案,然后散块边搞边更新
int get(int x){
return x%block==0?x/block:x/block+1;
}
inline void init(){
scanf("%d%d",&n,&m);
block=sqrt(n),siz=n%block==0?n/block:n/block+1;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),c[i]=a[i];
sort(a+1,a+n+1);
int q=unique(a+1,a+n+1)-a-1;
for(int i=1;i<=n;i++){
int pos=lower_bound(a+1,a+q+1,c[i])-a;
b[pos]=c[i];
c[i]=pos;
}
for(int i=1;i<=siz;i++){
memset(cnt,0,sizeof(cnt));
node x;
x.num=x.s=0;
for(int j=i;j<=siz;j++){
for(int k=(j-1)* block+1;k<=min(n,j * block);k++){
cnt[c[k]]++;
if((cnt[c[k]]>x.s)||(cnt[c[k]]==x.s&&c[k]<x.num)){
x.num=c[k];
x.s=cnt[c[k]];
}
}
p[i][j]=x;
}
}
for(int i=1;i<=siz;i++){
for(int j=1;j<=q;j++)sum[i][j]=sum[i-1][j];
for(int j=(i-1)* block+1;j<=min(n,i * block);j++)sum[i][c[j]]++;
}
}
int solve(int l,int r){
int ans=0;
int L=get(l),R=get(r);
if(R-L<=2){
for(int j=l;j<=r;j++)tot[c[j]]=0;
for(int j=l;j<=r;j++){
tot[c[j]]++;
if(tot[c[j]]>tot[ans]||(tot[c[j]]==tot[ans]&&ans>c[j]))ans=c[j];
}
}
else {
ans=p[L+1][R-1].num;
tot[ans]=0,vis[ans]=0;
for(int j=l;j<=min(n,L * block);j++)tot[c[j]]=0,vis[c[j]]=0;
for(int j=(R-1)* block+1;j<=r;j++)tot[c[j]]=0,vis[c[j]]=0;
for(int j=l;j<=min(n,L * block);j++)tot[c[j]]++;
for(int j=(R-1)* block+1;j<=r;j++)tot[c[j]]++;
int id,mx=0;
for(int j=l;j<=min(n,L * block);j++){
if(!vis[c[j]]){
vis[c[j]]=1;
int val=tot[c[j]]+sum[R-1][c[j]]-sum[L][c[j]];
if(mx < val||(mx==val&&id>c[j]))mx=val,id=c[j];
}
}
for(int j=(R-1)* block+1;j<=r;j++){
if(!vis[c[j]]){
vis[c[j]]=1;
int val=tot[c[j]]+sum[R-1][c[j]]-sum[L][c[j]];
if(mx < val||(mx==val&&id>c[j]))mx=val,id=c[j];
}
}
if(mx>tot[ans]+p[L+1][R-1].s||(mx==tot[ans]+p[L+1][R-1].s&&ans>id))ans=id;
}
last=b[ans];
return last;
}
由乃打扑克
Ynoi里唯一的良心题。参考昨天T2和,然后将求第
复杂度
具体地:
inline void init(){
read(n);read(m);
tt=log(block)/log(2)+1;siz=(n+block-1)/block;
for(re int i=1;i<=n;i++)read(a[i]);
for(re int i=1;i<=n;i++)b[i]=a[i];
for(re int i=1;i<=siz;i++)L[i]=R[i-1]+1,R[i]=L[i]+block-1;
R[siz]=n;
for(re int i=1;i<=siz;i++)for(re int j=L[i];j<=R[i];j++)pos[j]=i;
for(re int i=1;i<=siz;i++)sort(b+L[i],b+R[i]+1);
}
inline void init_only(int id){
for(re int i=L[id];i<=R[id];i++)b[i]=a[i]+tag[id];
for(re int i=L[id];i<=R[id];i++)a[i]+=tag[id];
sort(b+L[id],b+R[id]+1);
tag[id]=0;
}
inline void change(int l,int r,int k){
if(pos[l]==pos[r]){
for(re int i=l;i<=r;i++)a[i]+=k;
init_only(pos[l]);
return ;
}
for(re int i=pos[l]+1;i<pos[r];i++)tag[i]+=k;
for(re int i=l;i<=R[pos[l]];i++)a[i]+=k;
for(re int i=L[pos[r]];i<=r;i++)a[i]+=k;
init_only(pos[l]);init_only(pos[r]);
}
对于求排名,大块二分/倍增,小块暴力即可
大块有一个优化是提前判断一下本块最小值/最大值,如果最小值都比查找的值大直接跳,最大值比它小直接加上贡献跳过,可以避免二分一个块。加上这玩意跑得飞起。事实上随机数据下这个优化是很强的。
然后将第
inline int rk(int l,int r,int k){
re int res=0;
if(pos[l]==pos[r]){
for(int i=l;i<=r;i++)if(a[i]+tag[pos[l]]<k)res++;
return res;
}
for(re int i=l;i<=R[pos[l]];i++)if(a[i]+tag[pos[l]]<k)res++;
for(re int i=L[pos[r]];i<=r;i++)if(a[i]+tag[pos[r]]<k)res++;
for(re int i=pos[l]+1;i<pos[r];i++){
re int l=L[i]-1,c=k-tag[i]-1;
if(b[l+1]>c)continue;
if(b[R[i]]<=c){
res+=R[i]-L[i]+1;
continue;
}
for(re int p=tt;p>=0;--p){
if(l+(1<<p)>R[i])continue;
if(b[l+(1<<p)]<=c)l+=(1<<p);
}
res+=(l-L[i]+1);
}
return res;
}
注意这题有点小卡,所以需要提前限定二分范围,因为注意到题目说了每次改动不会超过
为了怕被卡,可以将
int query(int l,int r,int k){
re int L=-(10000*(T+3))<<1,R=(10000*(T+2))<<1;
while(L<R){
re int mid=L+R+1>>1;
if(rk(l,r,mid)+1>k)R=mid-1;
else L=mid;
}
if(L==-(10000*(T+3))<<1||L==(10000*(T+2))<<1)return -1;
return L;
}
signed main(){
ios::sync_with_stdio(false);
init();
while(m--){
re int opt,l,r,k;read(opt);read(l);read(r);read(k);
if(l>r)swap(l,r);
if(opt==2)change(l,r,k),++T;
else {
cout<<query(l,r,k)<<"\n";
}
}
}
注意利用评测机二分最优块长后,我发现块长设为330左右跑得很快
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!