莫队习题(Easy Ver)

​​​​​​[SNOI2017] 一个简单的询问

我们看看原式子

设 get(l,r,x) 表示x 在l,r中出现的次数:

       =)

这也太多有关量了我们尝试减少一点

式子变成了同一形式,

我们可以拆分为四个问题 ,,

问题的有关量减少了2个,将一个问题拆分成更多的问题,但减少了有关量这是在信息学中很重要的思想。

根据定义理解一下,现在他可不是一个区间而是两个前缀,就是:

求每出现的数分别在两前缀的次数乘积 再求和。

莫队好像只能解决区间问题吧,这是前缀怎么办?

莫队算法的本质是 1.改变你的暴力的顺序使你跑的更快。2.在平面是有一些点 点的哈弗曼距离的路径点覆盖尽可能小。和区间是绑定的吗?回答我!look my eyes! tell me why?

       回到正题,我们想暴力怎么做,我觉得我会先把一个前缀的数放桶里,遍历一遍另一前缀直接加。暴力想到,一个数增减 只需要增减另一个前缀的数。开两个桶结束咯。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int NR=4e5+5;
int n,m,k,s[NR],t[NR],u[NR],v[NR],l[NR],r[NR],MX,z[NR],ans[NR],a[NR],c[NR],d[NR],sum=0,g[NR];
bool cmp(int a,int b){
	return (l[a]/MX!=l[b]/MX)?(l[a]/MX<l[b]/MX):(((l[a]/MX)&1)?r[a]<r[b]:r[a]>r[b]);
} void Nw(int x,int y){
	++m;
	l[m]=x;r[m]=y;z[m]=m;
}
void Ad1(int x){
	c[x]++;sum+=d[x];
}
void Da1(int x){
	c[x]--;
	sum-=d[x];
}
void Ad2(int x){
	d[x]++;sum+=c[x];
}
void Da2(int x){
	d[x]--;sum-=c[x];
}
signed main(){
	cin>>n;MX=sqrt(n);
	for(int i=1;i<=n;i++)cin>>a[i];
	int sm;
	cin>>sm;
	for(int i=1;i<=sm;i++){
		cin>>s[i]>>t[i]>>u[i]>>v[i];
		Nw(t[i],v[i]);
	}
	sort(z+1,z+m+1,cmp);
	for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
		while(R<r[Z]){
			Ad1(a[++R]);
		}
		while(R>r[Z]){
			Da1(a[R--]);
		}
		while(L>l[Z]){
			Da2(a[L--]);
		}
		while(L<l[Z]){
			Ad2(a[++L]);
		}
		ans[Z]+=sum;
	}
	m=0;
	for(int i=1;i<=n;i++)c[i]=d[i]=0;
	for(int i=1;i<=sm;i++){
		Nw(s[i]-1,v[i]);
	}sum=0;
	sort(z+1,z+m+1,cmp);
	for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
		while(R<r[Z]){
			Ad1(a[++R]);
		}
		while(R>r[Z]){
			Da1(a[R--]);
		}
		while(L>l[Z]){
			Da2(a[L--]);
		}
		while(L<l[Z]){
			Ad2(a[++L]);
		}
		ans[Z]-=sum;
	}
	m=0;
	for(int i=1;i<=n;i++)c[i]=d[i]=0;
	for(int i=1;i<=sm;i++){
		Nw(t[i],u[i]-1);
	}sum=0;
	sort(z+1,z+m+1,cmp);
	for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
		while(R<r[Z]){
			Ad1(a[++R]);
		}
		while(R>r[Z]){
			Da1(a[R--]);
		}
		while(L>l[Z]){
			Da2(a[L--]);
		}
		while(L<l[Z]){
			Ad2(a[++L]);
		}
		ans[Z]-=sum;
	}
	m=0;
	for(int i=1;i<=n;i++)c[i]=d[i]=0;
	for(int i=1;i<=sm;i++){
		Nw(s[i]-1,u[i]-1);
	}sum=0;
	sort(z+1,z+m+1,cmp);
	for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
		while(R<r[Z]){
			Ad1(a[++R]);
		}
		while(R>r[Z]){
			Da1(a[R--]);
		}
		while(L>l[Z]){
			Da2(a[L--]);
		}
		while(L<l[Z]){
			Ad2(a[++L]);
		}
		ans[Z]+=sum;
	}
	for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
	return 0;
}

[国家集训队] 数颜色 / 维护队列

一稿40pts

带修莫队。

把时间轴看成地三维跑莫队。

#include<bits/stdc++.h>
using namespace std;
const int NR=1.5e5+5;
int n,m,l[NR],r[NR],MX,z[NR],ans[NR],a[NR],c[NR],sum=0,t[NR],p[NR],to[NR],fr[NR],tmp[NR];
bool cmp(int a,int b){
	if(l[a]/MX!=l[b]/MX)return l[a]/MX<l[b]/MX;
	else if(t[a]/MX!=t[b]/MX)return t[a]/MX<t[b]/MX; 
	else return r[a]<r[b];
} void Add(int x){
	c[x]++;
	if(c[x]==1)sum++;
} void Del(int x){
	c[x]--;
	if(c[x]==0)sum--;
}
signed main(){
	int q;
	cin>>n>>q;MX=pow(n,0.66);
	for(int i=1;i<=n;i++){
		cin>>a[i];tmp[i]=a[i];
	}
	for(int i=1;i<=q;i++){
		char C;
		cin>>C;
		if(C=='Q'){
			m++;
			cin>>l[m]>>r[m];t[m]=i;z[m]=m;
		}else{
			cin>>p[i]>>to[i];
			fr[i]=a[i];
			a[i]=to[i];
		}
	}
	sort(z+1,z+m+1,cmp);for(int i=1;i<=n;i++)a[i]=tmp[i];
	for(int i=1,Z,L=1,R=0,T=0;i<=m;i++){Z=z[i];
		while(T<t[Z]){
			++T;
			a[p[T]]=to[T];
		}
		while(T>t[Z]){
			--T;
			a[p[T]]=fr[T];
		}
		while(R<r[Z])Add(a[++R]);
		while(L>l[Z])Add(a[--L]);
		while(L<l[Z])Del(a[L++]); 
		while(R>r[Z])Del(a[R--]);
		ans[Z]=sum;
	}
	for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
	return 0;
}

[HNOI2016] 大数

一稿96pts

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int NR=2e5+5;
int p,n,m,MX,h[NR],a[NR],t[NR],cnt[NR],l[NR],r[NR],z[NR],sum=0,ans[NR],w[NR];
string S;
bool cmp(int a,int b){
	return (l[a]/MX!=l[b]/MX)?(l[a]/MX<l[b]/MX):(((l[a]/MX)&1)?r[a]<r[b]:r[a]>r[b]);
}
void Add(int x){
	sum+=cnt[x]++;
} void Del(int x){
	sum-=--cnt[x];
}
signed main(){
	cin>>p; 
	cin>>S;n=S.size();MX=sqrt(n);
	for(int i=0;i<n;i++)a[i+1]=S[i]-'0';w[0]=1;
	for(int i=1;i<=n;i++)w[i]=(w[i-1]*10)%p;
	for(int i=1;i<=n;i++)h[i]=(h[i-1]*10+a[i])%p;
	for(int i=1;i<=n;i++)t[i]=h[i]=(h[i]*w[n-i])%p;
	sort(t+1,t+n+2);
	int q=unique(t+1,t+n+2)-t-1;
	for(int i=1;i<=n;i++)h[i]=lower_bound(t+1,t+q+1,h[i])-t;
	cin>>m;
	for(int i=1;i<=m;z[i]=i,i++)cin>>l[i]>>r[i];
	sort(z+1,z+m+1,cmp);
	cnt[1]=1;h[0]=1;
	for(int i=1,Z,L=0,R=0;i<=m;i++){Z=z[i];
		while(R<r[Z])Add(h[++R]);
		while(L>=l[Z])Add(h[--L]);
		while(L<l[Z]-1)Del(h[L++]); 
		while(R>r[Z])Del(h[R--]); 
		ans[Z]=sum;
	}
	for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
	return 0;
} 

错音:没有意识到2和5与10不是互质的,使用了进制哈希导致错误。

做过字符串hash的人会想到进制哈希。一个区间[l,r]的值就是这个值被整除那就写成同余形式吧

     

                               

两边写成相同形式了,,
                                                 
,问题变成A题弱化版,看有几个和相同

注意到2和5需要特判,直接看末尾即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值