[BZOJ2038][2009国家集训队]小Z的袜子莫队

不说题意了 传送门biu~
就是推一推公式然后维护这些值 但是我是没推公式的 做完之后才推哇 不推也可以过的

要求区间[l,r][l,r]内选出两个相同颜色的概率,即求
( C 2 c n t 1 + C 2 c n t 2 + . . . + C 2 c n t k ) / C 2 r l + 1
其中cnti为颜色为ii的袜子有多少个,所以
c n t 1 + c n t 2 + c n t 3 + . . . + c n t k = r l + 1
所以刚才的式子就化为
( c n t 21 + c n t 22 + . . . + c n t 2 k ( r l + 1 ) ) / ( ( r l + 1 ) ( r l ) )

AC代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
int n,m;
ll s[50050],ans=0;
int c[50050],pos[50050];
struct node{int l,r,poi;ll a,b;}a[50050];
bool cmp(node x,node y){if(pos[x.l]==pos[y.l]) return x.r<y.r;  return x.l<y.l;}
bool cmpoi(node x,node y){return x.poi<y.poi;}
ll gcd(ll a,ll b)
{
    if(!b) return a;
    return gcd(b,a%b);
}
void push(int x){ans+=s[c[x]]++;}
void pop(int x){ans-=--s[c[x]];}
void solve()
{
    for(int i=1,l=1,r=0;i<=m;i++)
    {
        while(l<a[i].l) pop(l++);
        while(r>a[i].r) pop(r--);
        while(l>a[i].l) push(--l);
        while(r<a[i].r) push(++r);
        if(a[i].l==a[i].r)
        {
            a[i].a=0,a[i].b=1;
            continue;
        }
        a[i].a=ans;
        a[i].b=1ll*(a[i].r-a[i].l+1)*(a[i].r-a[i].l)/2;
        ll gcdd=gcd(a[i].a,a[i].b);
        a[i].a/=gcdd,a[i].b/=gcdd; 
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    int block=sqrt(n);
    for(int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a[i].l,&a[i].r);
        a[i].poi=i;
    }
    sort(a+1,a+1+m,cmp);
    solve();
    sort(a+1,a+1+m,cmpoi);
    for(int i=1;i<=m;i++) printf("%lld/%lld\n",a[i].a,a[i].b);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Ltb_Sjy1120/article/details/81367554