(HYSBZ - 2038)小Z的袜子(hose)(莫队)

这篇博客深入解析了洛谷题目P1494《小Z的袜子》,讲解了如何使用莫队算法解决袜子配对问题,涉及add和sub函数的逻辑,以及如何维护同种颜色袜子的方案数。通过实例演示了如何约分答案,适合学习和理解莫队在实际问题中的应用。

题目链接:P1494 [国家集训队]小Z的袜子 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目是中文的,我在这就不解释题意了。

这道题目考察的是莫队算法,还是老样子,模板不再多说,不熟悉的可以翻我之前的博客,下面主要说一下add函数以及sub函数。

我们用ans记录当前区间拿到的袜子是同一种颜色的可能情况数。

先说一下add函数,假如我们现在要加入的袜子颜色是x,那么情况数会怎样变化呢?显然不会影响从其他颜色中任选两只相同颜色的袜子的情况,他影响的只能是袜子颜色是x的情况,那又是如何影响的呢?显然只有选定当前加的袜子才会对之前的方案数产生影响,我们再从当前区间的袜子中随意选择一只,如果袜子颜色是x那么方案数就要加1,反之对ans无影响,也就是我们当前要加入的袜子颜色是x,那么对答案的贡献就是cnt[x],最后别忘了cnt[x]++。

下面来说一下sub函数,同样地,假如我们现在要删除的袜子颜色是x,那么情况数会怎样变化呢?显然不会影响从其他颜色中任选两只相同颜色的袜子的情况,他影响的只能是袜子颜色是x的情况,那又是如何影响的呢?也就是说答案里面有多少种情况是要删除的这个袜子贡献的呢?显然除了我们要删除的袜子,再从剩下的袜子种任选一个颜色为x的袜子均可构成一种方案数,所以我们要先进行cnt[x]--,再减去我们要删除的这个袜子所带来的贡献,也就是ans-=cnt[x]。

最后就是别忘了对满足题意的方案数以及总的情况数进行约分。

剩下的就是一个裸的莫队模板了:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
const int N=1e5+10;
typedef long long ll;
struct node{
	ll l,r,id,pl;
}p[N];
bool cmp(node a,node b)
{
	if(a.pl!=b.pl) return a.pl<b.pl;
	return a.r<b.r;
}
//ans记录当前取到相同颜色袜子的可能情况数 
ll n,m,ans;
ll sum1[N],sum2[N],cnt[N],a[N];
//sum1[i]存的是第i个询问的答案的分子
//sum2[i]存的是第i个询问的答案的分母 
ll gcd(ll a,ll b)
{
	if(a) return gcd(b%a,a);
	return b;
}
void add(ll x)
{
	ans+=cnt[x];
	cnt[x]++;
}
void sub(ll x)
{
	cnt[x]--;
	ans-=cnt[x];
}
int main()
{
	scanf("%lld%lld",&n,&m);
	ll pl=sqrt(n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld",&p[i].l,&p[i].r);
		p[i].id=i;
		p[i].pl=(p[i].l-1)/pl+1;
	}
	sort(p+1,p+m+1,cmp);
	ll l=1,r=0;
	for(int i=1;i<=m;i++)
	{
		while(l<p[i].l)
		{
			sub(a[l]);
			l++;
		}
		while(l>p[i].l)
		{
			l--;
			add(a[l]);
		}
		while(r<p[i].r)
		{
			r++;
			add(a[r]);
		}
		while(r>p[i].r)
		{
			sub(a[r]);
			r--;
		}
		ll t=(p[i].r-p[i].l+1)*(p[i].r-p[i].l)/2;
		if(ans==0) sum2[p[i].id]=1;//如果分子为0,则分母直接为1 
		else//约分 
		{
			ll GCD=gcd(ans,t);
			sum1[p[i].id]=ans/GCD;
			sum2[p[i].id]=t/GCD;
		}
	}
	for(int i=1;i<=m;i++)
		printf("%lld/%lld\n",sum1[i],sum2[i]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值