LGP9212 [CZCup R5-F] 「蓬莱人形」 学习笔记

LGP9212 [CZCup R5-F] 「蓬莱人形」 学习笔记

Luogu Link

符卡介绍

东方永夜抄Ex面十符。

原来如此是你啊。那个时候那个不可思议的人类。
那当然是应该死不了的啊。本来就是不老不死嘛。

题意简述

给定长为 \(n\) 的序列 \(A\)。有 \(m\) 次询问:给定二元组 \((x,y)\)、模数 \(p\)、区间 \([l,r]\),问有多少 \(i\in[l,r]\) 满足 \((a_i+x)\bmod p<(a_i+y)\bmod p\)

\(n,V\le 10^5\)\(m\le 5\times 10^5\)\(a_i,p\le V\)

做法解析

\(n,V\) 同阶,所以可能有 \(n,V\) 混用,见谅。

首先拆开这坨带模的询问。由于对“满足条件的元素数量”求和可差分,我们把问题差分为对 \([1,r]\) 的询问减去对 \([1,l)\) 的询问。另外让 \(x,y\)\(p\) 取模。然后考虑合法的 \(a_i\) 在模 \(p\) 意义下的值域范围。

  • \(x=y\) 时,无解。
  • \(x<y\) 时,要么都没取模,要么都取模了,则 \(a_i\in [0,p-y)\cup[p-x,p)\)
  • \(x>y\) 时,那要只有式子左边取模,则 \(a_i\in [p-y,p-x)\)

现在的询问形如:对 \(i\in[1,l]\),查询分布非常有规律的 \(O(\frac{V}{p})\) 个值域区间内有多少 \(a_i\)

你发现这玩意差不多是个根分的形式了。对于 \(p\le \sqrt{n}\) 的询问,我们显然可以开桶处理,在扫描整个区间的时候维护 \(buc_{i,j}\) 表示当前前缀里模 \(i\)\(j\) 的数量,询问的时候直接一个一个桶暴力取出来,总复杂度也是可接受的 \(O((n+m)\sqrt{n})\)

对于 \(p>\sqrt{n}\) 的询问呢?直接在值域上暴力修改查询的话,复杂度是 \(O(n\log V+m\sqrt{n}\log V)\) 的。然而一根号一劳葛是过不去的。你发现修改的复杂度远小于查询的复杂度,所以改用分块以平衡复杂度。此时一个修改 \(O(\sqrt{V})\),一个查询 \(O(1)\) 了,\(O((n+m)\sqrt{V})\),顺利通过。详见代码。

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=1e5+5,MaxM=5e5+5,bsiz=320;
int N,bnum,M,A[MaxN],L,R,X,Y,P,buc[bsiz+5][bsiz+5];
struct quer{int x,y,p,k,id;};vector<quer> Q[MaxM];
int bel[MaxN],lb[MaxN],rb[MaxN],sum[MaxN],laz[bsiz+5],ans[MaxM];
int bquery(int l,int r){
    if(l>Ocp5)return 0;minner(r,Ocp5);
    int bl=bel[l-1],br=bel[r];
    return sum[r]+laz[br]-sum[l-1]-laz[bl];
}
int main(){
    readis(N,M);bnum=pcedi(Ocp5,bsiz);
    for(int i=1;i<=N;i++)readi(A[i]);
    for(int i=1;i<=bnum;i++)lb[i]=rb[i-1]+1,rb[i]=bsiz*i;
    rb[bnum]=(Ocp5);for(int i=1;i<=Ocp5;i++)bel[i]=pcedi(i,bsiz);
    for(int i=1;i<=M;i++){
        readis(L,R,X,Y,P);X%=P,Y%=P;
        if(X!=Y)Q[R].push_back({X,Y,P,1,i}),Q[L-1].push_back({X,Y,P,-1,i});
    }
    for(int i=1;i<=N;i++){
        for(int j=1;j<=bsiz;j++)buc[j][A[i]%j]++;
        for(int j=A[i];j<=rb[bel[A[i]]];j++)sum[j]++;
        for(int j=bel[A[i]]+1;j<=bnum;j++)laz[j]++;
        for(auto [cx,cy,cp,ck,cid] : Q[i]){
            int tmp=0,lj=cp-max(cx,cy),rj=cp-min(cx,cy)-1;
            if(cp<=bsiz){
                for(int j=lj;j<=rj;j++)tmp+=buc[cp][j];
                if(cx<cy)tmp=i-tmp;
                ans[cid]+=ck*tmp;continue;
            }
            for(int pd=0;pd<=Ocp5;pd+=cp){
                tmp=bquery(pd+lj,pd+rj);
                if(cx<cy)tmp=bquery(pd,pd+cp-1)-tmp;
                ans[cid]+=ck*tmp;
            }
        }
    }
    for(int i=1;i<=M;i++)writi(ans[i]),puts("");
    return 0;
}
posted @ 2025-05-29 16:57  矞龙OrinLoong  阅读(13)  评论(0)    收藏  举报