51nod1667 概率好题

来源:互联网 发布:java类的定义格式 编辑:程序博客网 时间:2024/06/07 12:10

考虑计算方案数,令xi=aili(0xirili),得到

l1+x1?r2x2x1+x2?r2l1

x1x2并没有本质差别,用插板法解决。取等号时可以直接算,取小于等于时可以加入一个元素来放置多出来的部分。上限用容斥来解决。

#include<cstdio>#include<algorithm>using namespace std;#define LL long longconst int maxn=20,p=1000000007;int l1[maxn],l2[maxn],r1[maxn],r2[maxn],f[maxn],fac[maxn+10],inv[maxn+10],n1,n2,n;int inc(int x,int y){    x+=y;    return x>=p?x-p:x;}int dec(int x,int y){    x-=y;    return x<0?x+p:x;}int pow(int b,int k){    int r=1;    for (;k;k>>=1,b=(LL)b*b%p)        if (k&1) r=(LL)r*b%p;    return r;}int get(int n,int m){    int ret=1;    for (int i=n+m-1;i>=n+1;i--) ret=(LL)ret*i%p;    ret=(LL)ret*inv[m-1]%p;    return ret;}int calc(int n,int s){    int ret=0,s1,cnt,x;    for (int S=0;S<(1<<n);S++)    {        cnt=0;        s1=s;        for (int i=0;i<n;i++)            if (S&(1<<i))            {                cnt++;                s1-=f[i]+1;            }        if (s1<0) continue;        x=get(s1,n);        if (cnt&1) ret=dec(ret,x);        else ret=inc(ret,x);    }    return ret;}void solve(){    int x,y,z,w,s=1;    scanf("%d",&n1);    for (int i=1;i<=n1;i++) scanf("%d%d",&l1[i],&r1[i]);    scanf("%d",&n2);    for (int i=1;i<=n2;i++) scanf("%d%d",&l2[i],&r2[i]);    n=n1+n2;    w=0;    for (int i=1;i<=n1;i++) w=dec(w,l1[i]);    for (int i=1;i<=n2;i++) w=inc(w,r2[i]);    for (int i=1;i<=n1;i++) f[i-1]=r1[i]-l1[i];    for (int i=1;i<=n2;i++) f[i+n1-1]=r2[i]-l2[i];    y=calc(n,w);    f[n]=w;    x=calc(n+1,w);    for (int i=0;i<n;i++) s=(LL)s*(f[i]+1)%p;    z=dec(s,x);    x=dec(x,y);    s=pow(s,p-2);    x=(LL)x*s%p;    y=(LL)y*s%p;    z=(LL)z*s%p;    printf("%d %d %d\n",z,y,x);}int main(){    //freopen("a.in","r",stdin);    fac[0]=1;    for (int i=1;i<=maxn;i++) fac[i]=(LL)fac[i-1]*i%p;    for (int i=0;i<=maxn;i++) inv[i]=pow(fac[i],p-2);    int T;    scanf("%d",&T);    while (T--) solve();}