莫队模板以及常见的例题。 Fast Queries   F - XOR and Favorite Number 小Z的袜子

复杂度n^(1.5)

四个while循环中注意边界处的讨论。

精髓在add和del函数还有其排序的方式。

1.莫队求解l到r中不同数的个数。

1)      Fast Queries   题目链接:https://vjudge.net/problem/LightOJ-1188

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
#pragma GCC optimize(2)
const int maxx=1e6+19;
int pos[maxx];// 那一块 
int a[maxx];// 原数组 
struct node // 询问 
{
	int l,r,id; 
}Q[maxx]; 
bool cmp(node a,node b)
{
   if(pos[a.l]==pos[b.l]) return a.r<b.r;//先按l所在的块排,如果相等就按r排
    else return pos[a.l]<pos[b.l];
}
int ans[maxx];// 最终答案 
ll size,Ans=0;  // 每次转换得到的值. 
int flag[maxx*2]; // 标记数字出现的次数 
// 莫队算法由L-R 转换为 (l-1,R) (l+1,R) (L,R-1) (L,R+1) 
void add(int x)
{
	flag[a[x]]++;
	if(flag[a[x]]==1) Ans++;
}
void del(int x)
{
	flag[a[x]]--;
	if(flag[a[x]]==0) Ans--;
}
int main()
{
	int n,m,t;
	int L,R;
	scanf("%d",&t);
	int p=0;
	while(t--)
	{
		L=1,R=0;
		Ans=0;
		memset(flag,0,sizeof(flag));
		scanf("%d%d",&n,&m);
		size=sqrt(n);
		for(int i=1;i<=n;i++) 
		{
			scanf("%d",&a[i]);
			pos[i]=i/size;//别写错地方了!》。。。。 
		}
			
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&Q[i].l,&Q[i].r);
			Q[i].id=i;
		}
		sort(Q+1,Q+1+m,cmp);
		for(ll i=1;i<=m;i++)
		{
			while(R<Q[i].r)
	        {
	            R++;
	            add(R);
	        }
	        while(L>Q[i].l) ///前缀和L+1>Q[i].l
	        {
	            L--;
	            add(L);
	        }
	        while(L<Q[i].l)///前缀和L+1<Q[i].l
	        {
	            del(L);
	            L++;
	        }
	        while(R>Q[i].r)
	        {
	            del(R);
	            R--;
	        }
	        ans[Q[i].id]=Ans;
		}
		printf("Case %d:\n",++p);
		for(int i=1;i<=m;i++)
		{
			printf("%d\n",ans[i]);
		}
	}
	return 0;
}

 

2.莫队求解l到r中有多少个异或和等于k

1) F - XOR and Favorite Number 题目链 https://vjudge.net/contest/326214#problem/F

 

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
// ai^ai+1...^aj=sumi-1^sumj   sum为异或前缀和. 
// 莫队算法由L-R 转换为 (l-1,R) (l+1,R) (L,R-1) (L,R+1) 
typedef long long ll;
#pragma GCC optimize(2)
const int maxx=1e6+19;
int pos[maxx];// 那一块 
ll a[maxx];// 原数组 
struct node // 询问 
{
	int l,r,id; 
}Q[maxx]; 
bool cmp(node a,node b)
{
   if(pos[a.l]==pos[b.l]) return a.r<b.r;//先按l所在的块排,如果相等就按r排
    else return pos[a.l]<pos[b.l];
}
ll ans[maxx];// 最终答案 
ll size,Ans=0;  // 每次转换得到的值. 
int flag[maxx*2]; // 标记数字出现的次数 
int n,m,t,k;
void add(int x)
{
	Ans+=flag[a[x]^k];//  假设这一段新增加的数量为x  次数为a也就是flag[ax]的个数   则a^x=k   两边同时异或a 得x=k^a 
	flag[a[x]]++;
}
void del(int x)
{
	flag[a[x]]--;
	Ans-=flag[a[x]^k];
}
int main()
{
	int L,R;
	while(~scanf("%d%d%d",&n,&m,&k))
	{
		L=1,R=0;
		Ans=0;
		memset(flag,0,sizeof(flag));
		
		size=sqrt(n);
		for(int i=1;i<=n;i++) 
		{
			scanf("%d",&a[i]);
			a[i]^=a[i-1];
			pos[i]=i/size;//别写错地方了!》。。。。 
		}
		flag[0]=1;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&Q[i].l,&Q[i].r);
			Q[i].id=i;
		}
		sort(Q+1,Q+1+m,cmp);
		for(int i=1;i<=m;i++)
		{
	        while(L<Q[i].l)//    ai^ai+1...^aj=sumi-1^sumj 答案=l-1到r的值 
	        {
	        	del(L-1);
	        	L++;
			}
			while(L>Q[i].l)
			{
				L--;
				add(L-1);
			}
			while(R<Q[i].r)
			{
				R++;
				add(R);
			}
			while(R>Q[i].r)
			{
				del(R);
				R--;
			}
	        ans[Q[i].id]=Ans;
		}
		
		for(int i=1;i<=m;i++)
		{
			printf("%lld\n",ans[i]);
		}
	}
	return 0;
}

3.小Z的袜子

就是组合数求概率然后用莫队处理一下下。

 

#include<bits/stdc++.h> 
using namespace std;
const long long mod=1e9+7;
const int maxx=5e4+19;
#define ll long long
ll a[maxx];
struct node
{
	ll l,r,id;
}Q[maxx];
ll pos[maxx];
ll size=0,Ans=0;
ll ans1[maxx],ans2[maxx];
ll flag[maxx];
ll L=1,R=0;
void add(ll x)
{
	flag[a[x]]++;
	if(flag[a[x]]>=2)
	{
		ll p=flag[a[x]]; 
		Ans+=((p*(p-1))/2);// 比如由c32变成了 c42  Ans变化就是加上c42减去c32; 
		Ans-=(((p-1)*(p-2))/2);
	}
}
void del(ll x)
{
	// 和加思路一样的。 
	if(flag[a[x]]>=2) // 如果对答案有贡献 
	{
		ll p=flag[a[x]]; 
		Ans-=((p*(p-1))/2);
		Ans+=(((p-1)*(p-2))/2);
	}
	flag[a[x]]--;
}
bool cmp(node a,node b)
{
	if(pos[a.l]==pos[b.l]) return a.r<b.r;
	return pos[a.l]<pos[b.l];
}
ll gcd(ll a,ll b)
{
	if(b==0) return a;
	return gcd(b,a%b);
}
int main()
{
   	ll n,m,k,i,j,t;
 	scanf("%lld%lld",&n,&m);
 	size=sqrt(n*1.0);
 	for(i=1;i<=n;i++) 
	{
	 	scanf("%lld",&a[i]);
	 	pos[i]=(int)i/size;
	}
 	for(i=1;i<=m;i++)
 	{
	 	scanf("%lld%lld",&Q[i].l,&Q[i].r);
		Q[i].id=i;
	}
 	sort(Q+1,Q+1+m,cmp);
 	for(i=1;i<=m;i++)
 	{
 		while(L<Q[i].l)
 		{
 			del(L);
			L++;	
		}
		while(L>Q[i].l)
		{
			L--;
			add(L);
		}
 		while(R<Q[i].r)
 		{
 			R++;
 			add(R);
		}
		while(R>Q[i].r)
		{
			del(R);
			R--;
		}
 		ans1[Q[i].id]=Ans;
		ans2[Q[i].id]=(R-L)*(R-L+1)/2; // 分母. 
	}
 	for(i=1;i<=m;i++)
 	{
 		ll x=gcd(ans1[i],ans2[i]);
 		if(ans1[i]==0)
 		{
 			printf("0/1\n");
		}
		else 
 		printf("%lld/%lld\n",ans1[i]/x,ans2[i]/x);
	}
    return 0;
}

4、

裸的带修改莫队模板,自己打的老是不对,只能用网上性能高的了,先不看这个带修改的了。

。询问的还是l到r中有多少个不同的数加修改。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+50;
int a[MAXN],cnt[MAXN],Ans[MAXN];
struct pnode{ int id,l,r,x1,x2,t; } p[MAXN];
struct qnode{ int x,pre,nxt; } q[MAXN];
int compare(pnode x,pnode y)
{ return (x.x1<y.x1)||(x.x1==y.x1&&x.x2<y.x2)||(x.x1==y.x1&&x.x2==y.x2&&x.t<y.t); }
inline int read()
{
	int f=1,x=0; char c=getchar();
	while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
	while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
	return x*f;
}
inline char get_ch()
{
	char c=getchar();
	while (c<'A'||c>'Z') c=getchar();
	return c;
}
int main()
{
	int n=read(),m=read(),numq=0,nump=0;
	for (int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<=m;i++)
	{
		char c=get_ch(); 
		int x=read(),y=read();
		if (c=='Q') p[++nump]=(pnode){nump,x,y,0,0,numq};	
		else
		{
			q[++numq]=(qnode){x,a[x],y};
			a[x]=y;
		}
	}
	int Size=ceil(exp((log(n)+log(numq))/3));
	for (int i=1;i<=nump;i++) p[i].x1=(p[i].l-1)/Size+1,p[i].x2=(p[i].r-1)/Size+1;
	sort(p+1,p+nump+1,compare);
	
	int L=1,R=0,T=numq,num=0;
	memset(cnt,0,sizeof cnt);
	for (int i=1;i<=nump;i++)
	{
		while (L<p[i].l) cnt[a[L]]--,num-=(cnt[a[L]]==0),L++;
		while (L>p[i].l) L--,cnt[a[L]]++,num+=(cnt[a[L]]==1);
		while (R<p[i].r) R++,cnt[a[R]]++,num+=(cnt[a[R]]==1);
		while (R>p[i].r) cnt[a[R]]--,num-=(cnt[a[R]]==0),R--;
		
		while (T>p[i].t) 
		{
			qnode Q=q[T];
			if (p[i].l<=Q.x&&Q.x<=p[i].r)
			{
				cnt[Q.nxt]--; num-=(cnt[Q.nxt]==0);
				cnt[Q.pre]++; num+=(cnt[Q.pre]==1);
			}
			a[Q.x]=Q.pre;
			T--;
		}
		while (T<p[i].t) 
		{
			T++;
			qnode Q=q[T];
			if (p[i].l<=Q.x&&Q.x<=p[i].r)
			{
				cnt[Q.pre]--; num-=(cnt[Q.pre]==0);
				cnt[Q.nxt]++; num+=(cnt[Q.nxt]==1);
			}
			a[Q.x]=Q.nxt;
		}
		Ans[p[i].id]=num;
	}
	for (int i=1;i<=nump;i++) printf("%d\n",Ans[i]);
	return 0;
} 

5.题意:NPY有n个女朋友,编号从1到n,分布在各个班级中,给出m个区间[Li,Ri],每次NPY都想找区间[Li,Ri]中的女朋友约会,要求求出班级排列不同的约会的方案有几种(如果两个女朋友在同一个班,那么交换约会顺序的话还是算一种方案)

先考虑暴力求解的状况:

假设在区间[L,R]之间,cnt[1],cnt[2],cnt[3]…cnt[i]…cnt[maxn]分别表示区间[L,R]之间在班级 i 中的女朋友的数量,那么此时方案数量是:

 

现在简单推导一下区间改变了之后对方案数的影响:

如果区间改变后一个值的出现次数增加了一次:

最终方案数会改变成ANS*(R-L+1+1)/(cnt[i]+1)

而如果其中一个值出现次数减少了一次

方案数变成ANS*cnt[i]/(R-L+1)

(ANS为已知区间的方案数)

由于1000000007是个大质数,所以对于除法的取模采取乘逆元的办法

先根据费马小定理预处理出1~30000之间所有数对1000000007的逆元,然后莫队分块计算就好了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;
const int maxn = 3e4+7;
int block;
struct QUERY{
	int lt,rt,id;
	bool operator < (const QUERY &x){
		if(lt/block==x.lt/block) return rt<x.rt;
		else return lt<x.lt;
	}
}query[maxn];
int T,n,m,seq[maxn];
LL inv[maxn],res[maxn],ans,cnt[maxn];
LL qpow_mod(LL a,LL b){
	LL tmp = 1;
	while(b){
		if(b&1) tmp = tmp*a%mod;
		b>>=1;
		a = a*a%mod;
	}
	return tmp;
}
void inv_table(){
	for(int i=1;i<maxn;i++) inv[i] = qpow_mod(i,mod-2);
}
inline void add(LL d,LL x){
	ans = ans*(d+1)%mod*inv[++cnt[x]]%mod;
}
inline void sub(LL d,LL x){
	ans = ans*(cnt[x]--)%mod*inv[d]%mod;
}
void solve(){
	int l=1,r=0;
	ans = 1;
	for(int i=1;i<=m;i++){
		while(query[i].lt<l){
			LL d = r-l+1;
			add(d,seq[--l]);
		}
		while(query[i].rt>r){
			LL d = r-l+1;
			add(d,seq[++r]);
		}
		while(query[i].lt>l){
			LL d = r-l+1;
			sub(d,seq[l++]);
		} 
		while(query[i].rt<r){
			LL d = r-l+1;
			sub(d,seq[r--]);
		} 
		
		res[query[i].id] = ans;
	}
}
int main(){
	inv_table();
	scanf("%d",&T);
	while(T--){
		memset(cnt,0,sizeof(cnt));
		scanf("%d %d",&n,&m);
		block = sqrt(n);
		for(int i=1;i<=n;i++) scanf("%d",&seq[i]);
		for(int i=1;i<=m;i++){
			scanf("%d %d",&query[i].lt,&query[i].rt);
			query[i].id = i;
		}
		sort(query+1,query+1+m);
		solve();
		for(int i=1;i<=m;i++) printf("%Ild\n",res[i]);
	}
	return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值