bzoj-2038 小Z的袜子 hose

141 篇文章 0 订阅
88 篇文章 0 订阅

题意:

给出一个长度为n的序列,每次询问一个区间[l,r];

查询在这个区间中取出两个数恰好相等的概率;

每个数大小在[0,n]内,概率用既约分数表示;


题解:
考虑一个区间的答案,显然是合法方案数/取数的所有可能;

也就是 ∑C[同种数字个数][2]/C[r-l+1][2];

但是这个东西对一次询问的处理复杂度是O(r-l+1)的;

那么考虑上莫队算法,处理这样的区间问题;

很容易发现每次修改边界可以做到O(1)完成;

void update(int x,int op) 
{ 
    now-=C[s[a[x]]][2]; 
    s[a[x]]+=op; 
    now+=C[s[a[x]]][2]; 
} 

C[i][j]是组合数,s[x]是当前区间x的数量,now是当前答案;

然后将询问排序处理,第一关键字左端点所在块,第二关键字右端点

当块的大小为√n时,可以证明复杂度不会超过O(n√n);

然后每个区间转移到下一个区间就是有复杂度保证的暴力咯;

码量似乎不算太巨大,思想也比较简单,很神的暴力算法;



代码:



#include<math.h> 
#include<stdio.h> 
#include<string.h> 
#include<algorithm> 
#define N 51000 
using namespace std; 
struct query 
{ 
    int pos,l,r,no; 
}Q[N]; 
int a[N],s[N],ans_up[N],ans_do[N],C[N][3],now; 
bool cmp(query a,query b) 
{ 
    if(a.pos==b.pos) 
    return a.r<b.r; 
    return a.pos<b.pos; 
} 
int gcd(int a,int b) 
{ 
    if(!a||!b)  return a?a:b; 
    int t=a%b; 
    while(t) 
    { 
        a=b,b=t; 
        t=a%b; 
    } 
    return b; 
} 
void update(int x,int op) 
{ 
    now-=C[s[a[x]]][2]; 
    s[a[x]]+=op; 
    now+=C[s[a[x]]][2]; 
} 
int main() 
{ 
    int n,m,k,i,j,l,r; 
    scanf("%d%d",&n,&m); 
    int bk=sqrt(n); 
    for(i=1;i<=n;i++) 
        scanf("%d",a+i); 
    for(i=0;i<=n;i++) 
    { 
        C[i][0]=1; 
        for(j=1;j<=2;j++) 
            C[i][j]=C[i-1][j]+C[i-1][j-1]; 
    } 
    for(i=1;i<=m;i++) 
    { 
        scanf("%d%d",&Q[i].l,&Q[i].r); 
        Q[i].no=i,Q[i].pos=Q[i].l/bk; 
    } 
    sort(Q+1,Q+m+1,cmp); 
    l=0,r=0,now=0,s[50001]=1,a[0]=50001; 
    for(i=1;i<=m;i++) 
    { 
        while(l<Q[i].l)  update(l++,-1); 
        while(l>Q[i].l)  update(--l, 1); 
        while(r<Q[i].r)  update(++r, 1); 
        while(r>Q[i].r)  update(r--,-1); 
        ans_up[Q[i].no]=now; 
        ans_do[Q[i].no]=C[r-l+1][2]; 
    } 
    for(i=1;i<=m;i++) 
    { 
        k=gcd(ans_up[i],ans_do[i]); 
        printf("%d/%d\n",ans_up[i]/k,ans_do[i]/k); 
    } 
    return 0; 
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值