莫队
核心思路:离线算法,把每次的询问先记录下来,按一定的顺序排序,然后暴力从上一个区间转移到下一个区间。
复杂度:
- 当 \(n\) ,\(m\) 同阶时,块长取 \(\sqrt n\) 时,复杂度为 \(O(n \sqrt n)\) ;
- 当 \(m < n\) 时,块长取 \(\displaystyle \frac{n}{\sqrt{m}}\) 时,复杂度为 \(O(n \sqrt m)\) .
例题 小 Z 的袜子
分析可见 oi-wiki ( /xyx
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m,k,a[N],ans[N],s[N],ps[N],l,r,blo,sm;
ll L[N],R[N];
struct node{
int l,r,sum;
void in(int x){scanf("%d%d",&l,&r);sum=x;L[x]=l,R[x]=r;}
}qr[N];
bool cmp(node x,node y){return ps[x.r]==ps[y.r]?x.l<y.l:x.r<y.r;}
int gx(int x){return s[a[x]]*(s[a[x]]-1)/2;}
int main()
{
scanf("%d%d",&n,&m);
blo=n/sqrt(m*2/3);l=1;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),ps[i]=(i-1)/blo+1;
for(int i=1;i<=m;i++) qr[i].in(i);
sort(qr+1,qr+m+1,cmp);
for(int i=1;i<=m;i++)
{
while(l<qr[i].l) sm-=gx(l),s[a[l]]--,sm+=gx(l),l++;
while(l>qr[i].l) l--,sm-=gx(l),s[a[l]]++,sm+=gx(l);
while(r<qr[i].r) r++,sm-=gx(r),s[a[r]]++,sm+=gx(r);
while(r>qr[i].r) sm-=gx(r),s[a[r]]--,sm+=gx(r),r--;
ans[qr[i].sum]=sm;
}
for(int i=1;i<=m;i++)
{
if(L[i]==R[i]) {puts("0/1");continue;}
int gcd=__gcd(ans[i]*1ll,(R[i]-L[i]+1)*1ll*(R[i]-L[i])/2*1ll);
printf("%d/%lld\n",ans[i]/gcd,(R[i]-L[i]+1)*1ll*(R[i]-L[i])/2*1ll/gcd);
}
return 0;
}
如果直接求一个序列的答案,发现并不好求。于是考虑求以 \(l\) 为左端点(右端点类似)所造成的贡献,显然,当满足 \(\displaystyle \frac{ s[r]−s[l−1]}{10^{n−r}}\equiv0\pmod{p}\),就找到了一个合法的区间(\(s[i]\) 表示由前 \(i\) 个数和后 \(n−i\) 个 \(0\) 构成的数字)。这样我们发现,假设我们已知一个区间的答案,可以做到 \(O(1)\) 移动一个单位的端点。而我们要求的,实际上就是区间中等于某个数的位置个数。可以将其离散化之后,用莫队+桶维护。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
inline int read()
{
int x=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if (ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*w;
}
#define int long long
inline int lread()
{
int x=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if (ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*w;
}
#undef int
int q,ps[N],n,cnt[N],tot,vi[N];
char s[N];ll res[N],ans[N],nw,v[N],t[N],p,x,fc=1;
struct node{
int l,r,id;
bool operator <(const node &y)const{
return ps[l]==ps[y.l]?r<y.r:ps[l]<ps[y.l];
}
}qr[N];
void tepan()
{
int l,r;
for(int i=1;i<=n;i++)
{
res[i]=res[i-1],cnt[i]=cnt[i-1];
if((s[i]-'0')%p==0) res[i]+=i,cnt[i]++;
}
while(q--)
l=read(),r=read(),printf("%lld\n",res[r]-res[l-1]-(ll)(cnt[r]-cnt[l-1])*(l-1));
exit(0);
}
void upd(int x)
{
nw-=(ll)(cnt[v[x]]-1)*cnt[v[x]]/2;
if(!vi[x]) cnt[v[x]]++;else cnt[v[x]]--;
vi[x]^=1;nw+=(ll)(cnt[v[x]]-1)*cnt[v[x]]/2;
}
int main()
{
p=lread();scanf("%s",s+1);q=read();
n=strlen(s+1);int sz=sqrt(n)+1;
if(p==2||p==5) tepan();
for(int i=1;i<=n;i++) ps[i]=(i-1)/sz+1;
for(int i=n;i;i--)
x=(x+(ll)(s[i]-'0')*fc)%p,fc=(ll)fc*10%p,v[i]=t[i]=x;
v[++n]=t[n]=0;
sort(t+1,t+n+1);tot=unique(t+1,t+n+1)-t-1;
for(int i=1;i<=n;i++) v[i]=lower_bound(t+1,t+tot+1,v[i])-t;
for(int i=1;i<=q;i++) qr[i].l=read(),qr[i].r=read()+1,qr[i].id=i;
sort(qr+1,qr+q+1);
int l=1,r=0;
for(int i=1;i<=q;i++)
{
while(r<qr[i].r) upd(++r);
while(l>qr[i].l) upd(--l);
while(r>qr[i].r) upd(r--);
while(l<qr[i].l) upd(l++);
ans[qr[i].id]=nw;
}
for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
return 0;
}
\(\texttt{End.}\)

浙公网安备 33010602011771号