莫队

oi-wiki

核心思路:离线算法,把每次的询问先记录下来,按一定的顺序排序,然后暴力从上一个区间转移到下一个区间。

复杂度:

  1. \(n\) ,\(m\) 同阶时,块长取 \(\sqrt n\) 时,复杂度为 \(O(n \sqrt n)\)
  2. \(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.}\)

posted @ 2021-08-20 11:53  lxgw  阅读(136)  评论(0)    收藏  举报