【组合数学】LGP3330(ZJOI2011)+UOJ#209

来源:互联网 发布:星际争霸2 剧情知乎 编辑:程序博客网 时间:2024/06/07 05:31

LGP3330 看电影(MOVIE)

原题地址

【题目大意】
一共有n个人,k个格子(格子标号为1~k)
循环n轮,每轮随机一个k以内的整数(设为m),若m~k这些格子里有空的,就把一个人放到格子里,否则这个人将站着。

问全部人都坐着的概率是多少。

【题目分析】
一眼组合数学,实在不会可以考虑打表。
组合数学中经典的古典概型,概率等于合法的方案数除以总方案数。

【解题思路】
我们易得总方案数=KN
 
合法方案数的计算:考虑在最后面加一个座位,然后所有的座位连成一个环。
这样我们保证不论怎么放,最后都会有一个空的位置,那么我们从这个空的位置断开(将它丢到),必然是一个合法的情况。

现在那么总方案数等于(K+1)N,每种环都算了(K+1)次,所以除以(K+1),最后抽调一个空白的座位来构造一个合法的方案,所以要乘(NK+1)。

综上所述,答案就是:

(K+1)N1×(NK+1)KN

要用高精度 。

【代码】

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int MAXN=205;const int MAXL=505;int prinum,T,n,k,len;int a[MAXL],pri[MAXN],ans[MAXN],ans2[MAXN];int p[MAXN][MAXN];bool ispri[MAXN];inline void get_prime(){    memset(ispri,true,sizeof(ispri));    ispri[1]=false;    for(int i=2;i<MAXN;++i)    {        if(ispri[i])        {            ++prinum;            pri[prinum]=i;        }        for(int j=1;j<=prinum && pri[j]*i<MAXN;++j)        {            ispri[pri[j]*i]=false;            if(i%pri[j]==0)                break;        }    }}inline void get_num(){    int x;    for(int i=1;i<MAXN;++i)    {        x=i;        for(int j=1;j<=prinum;++j)            while(x%pri[j]==0)            {                p[i][pri[j]]++;                x/=pri[j];            }    }}inline void _reset(){    memset(ans,0,sizeof(ans));    memset(ans2,0,sizeof(ans2));}inline void mul(int x){    if(len==0)    {        len=1;a[len]=x;        for(len++;a[len];)        {            a[len+1]+=a[len]/10;            a[len++]%=10;        }        --len;    }    else    {        for(int i=1;i<=len;++i)            a[i]*=x;        for(int i=1;i<=len;++i)        {            a[i+1]+=a[i]/10;            a[i]%=10;           }        for(len++;a[len];)        {            a[len+1]+=a[len]/10;            a[len++]%=10;           }        --len;    }}inline void get_ans(){   /*  for(int i=1;i<10;++i)        printf("%d ",ans[i]);    printf("\n");    for(int i=1;i<10;++i)        printf("%d ",ans2[i]);    printf("\n");*/    len=0;    memset(a,0,sizeof(a));    for(int i=1;i<MAXN;++i)        for(int j=1;j<=ans[i];++j)            mul(i);    if(!len)        printf("1");    for(int i=len;i;--i)        printf("%d",a[i]);    printf(" ");        len=0;      memset(a,0,sizeof(a));    for(int i=1;i<MAXN;++i)        for(int j=1;j<=ans2[i];++j)            mul(i);    if(!len)        printf("1");    for(int i=len;i;--i)        printf("%d",a[i]);    printf("\n");}inline void solve(){       scanf("%d%d",&n,&k);    if(n>k)    {        printf("0 1\n");        return;    }    for(int i=1;i<=k+1;++i)        ans[i]=p[k+1][i]*(n-1);    if(k-n>0)        for(int i=1;i<=k-n+1;++i)            ans[i]+=p[k-n+1][i];    if(k>1)        for(int i=1;i<=k;++i)        {            ans2[i]=p[k][i]*n-ans[i];            ans[i]-=p[k][i]*n;        }    get_ans();}int main(){    freopen("LGP3330.in","r",stdin);    freopen("LGP3330.out","w",stdout);    get_prime();    get_num();    scanf("%d",&T);    while(T--)    {        _reset();        solve();    }    return 0;}

UOJ#209. 【UER #6】票数统计

原题地址

【题目大意】
一共有n个数字 ,每个数字要么是0,要么是1。
有m个限制(xy),表示前x个数字里有y个1,后y个数字里有x个1
求满足所有限制的序列个数。

n5000m10009982443535

【题目分析】
都是骗人的!

【解题思路】
当x!=y的时候,我们已经确定了这个限制是对前缀还是后缀的。
当x=y的时候,我们只需要保留最大的那个x就行了。

然后,一波组合数学。
答案是x=y是前缀限制的个数+后缀限制的个数-前后缀都限制的个数。

那么,现在就是有一堆前后缀的限制,让你求方案数。

如果只有前缀或只有后缀就是一个划分成m个区间的组合数学。

如果两个都有就把前缀转成后缀或者把后缀转成前缀。
枚举1的总和就行了。

【代码】

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int MAXN=5010;const int mod=998244353;int n,m,ans,tot,lz,T;int C[MAXN][MAXN];struct Tnode{    int x,y,bz,ty;    Tnode(){}    Tnode(int xx,int yy,int bzz,int tyy)    {        x=xx;y=yy;        bz=bzz;ty=tyy;    }};Tnode a[MAXN],p[MAXN];inline void get_C(){    C[0][0]=1;    for(int i=1;i<MAXN-5;++i)    {        C[i][0]=1;        for(int j=1;j<=i;++j)            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;    }}inline bool cmp(Tnode A,Tnode B){    return A.x<B.x;}inline void init(){    scanf("%d%d",&n,&m);    tot=ans=lz=0;    for(int i=1;i<=m;++i)    {        int x,y;        scanf("%d%d",&x,&y);        if(x>y)            a[++tot]=(Tnode){x,y,0,0};        else        if(x<y)            a[++tot]=(Tnode){n-y,x,1,0};        else            lz=max(lz,x);    }    a[++tot]=(Tnode){lz,lz,0,1};    a[++tot]=(Tnode){n-lz,lz,1,2};    sort(a+1,a+tot+1,cmp);//  for(int i=1;i<=tot;++i)//      printf("%d %d %d %d\n",a[i].x,a[i].y,a[i].bz,a[i].ty);}inline void solve(){    for(int typ=1;typ<=3;++typ)    {        for(int i=1;i<=n;++i)        {            int cnt=0;bool flag=true;            for(int j=1;j<=tot;++j)            {                if(a[j].ty==typ)                    continue;                p[++cnt].x=a[j].x;                if(a[j].bz)                    p[cnt].y=i-a[j].y;                else                    p[cnt].y=a[j].y;                if(p[cnt].y<0 || p[cnt].y>i)                {                    flag=false;                    break;                }                if(p[cnt].x<p[cnt].y)                {                    flag=false;                    break;                }                if(p[cnt].x-p[cnt-1].x<p[cnt].y-p[cnt-1].y)                {                    flag=false;                    break;                }                if(p[cnt].y-p[cnt-1].y<0)                {                    flag=false;                    break;                }            }            if(!flag)                continue;            int sum=1;            for(int j=1;j<=cnt;++j)                sum=1ll*sum*C[p[j].x-p[j-1].x][p[j].y-p[j-1].y]%mod;            sum=1ll*sum*C[n-p[cnt].x][i-p[cnt].y]%mod;//              printf("sum:%d\n",sum);            if(typ<=2)                ans=(ans+sum)%mod;            else                ans=(ans-sum+mod)%mod;        }    }    printf("%d\n",ans);}int main(){//  freopen("UOJ209.in","r",stdin);//  freopen("UOJ209.out","w",stdout);    get_C();    scanf("%d",&T);    while(T--)    {        init();        solve();    }    return 0;}
原创粉丝点击