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

被折叠的 条评论
为什么被折叠?



