zoukankan      html  css  js  c++  java
  • luogu P3223 [HNOI2012]排队

    LINK:排队

    原谅我没学过组合数学 没有高中数学基础水平...

    不过凭着隔板法的应用还是可以推出来的。

    首先考虑女生 发现一个排列数m! 两个女生不能相邻 那么理论上来说存在无解的情况 而这道题好些没有特意去说明无解输出什么。

    这里还是尽量特判一下吧。然后考虑要在m-1个空隙中插入人 人可以是男生或者是老师。这里我们假设老师不区别对待

    那么就是 n+2个人 我们先强制插入了m-1个人 剩余n+3-m个人 剩下的人 此时存在m+1个空隙了 隔板法可知此时的方案数为C(n+3-m+m+1-1,m+1-1);

    化简为C(n+3,m); 可以发现此时老师和男生的排列可以任意 所以要乘上一个(n+2)!。

    我们考虑不合法的方案 两个老师在一起了 把老师当成一个人 单独乘上2! 再按照刚才计算方案的方法计算即可。

    第一步的方案数:m!(n+2)!C(n+3,m) 减去第二部的方案数:2!m!(n+1)!C(n+2,m);

    可以发现我们已经统计了所有可能发生的情况。

    唯一可能发生的情况就是无解。

    还有一种思考方式 先考虑女孩 再把男孩插入其中 最后插如老师 老师此时带来的贡献为(n+m+1)*(n+m);

    但是这种方法存在漏洞 这样会出现男孩不够m-1时需要老师 而我们老师插入的时候已经默认完全合法了。

    当n>=m-1时是可行的 这个方法要特判更多 没有上述方法特判无解来的快。

    还有一种解法:先考虑老师 考虑没有限制 n+2个男孩 m个女生 有n+3个空隙可以放女生 此时方案数为C(n+3,m)

    总方案为 m!(n+2)!C(n+3,m) 需要减掉的方案和刚才一样 这种是插空法 和我们的隔板法有异曲同工之妙 但是比隔板法要简单。

    主要运用的是 找到空隙插入而并非强制让空隙不存在。

    两种方法是等效的 我们高兴的是 遇到这种题目 思路不同但是得到的确实相同。

    值得注意的是需要使用高精度。

    高精除以高精显然要难写的很多...(我也不太会写 所以这里还是考虑分解质因数约分 最后变成高精乘单精 高精减高精。

    值得注意的是 可以练习一下压位操作 我们一次乘法最大数字2000 所以可以压上15位 这样可以让程序跑的更快一点。

    //#include<bitsstdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cmath>
    #include<cctype>
    #include<cstdlib>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<vector>
    #include<algorithm>
    #include<utility>
    #include<bitset>
    #include<set>
    #include<map>
    #define ll long long
    #define db double
    #define ldb long double
    #define pb push_back
    #define get(x) x=read()
    #define gt(x) scanf("%d",&x)
    #define put(x) printf("%d
    ",x)
    #define putl(x) printf("%lld
    ",x)
    #define gc(a) scanf("%s",a+1);
    #define rep(p,n,i) for(RE int i=p;i<=n;++i)
    #define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
    #define fep(n,p,i) for(RE int i=n;i>=p;--i)
    #define pii pair<int,int> 
    #define F first
    #define S second
    #define mk make_pair
    #define mod 1000003
    #define RE register
    #define gf(x) scanf("%lf",&x)
    #define pf(x) ((x)*(x))
    #define ull unsigned long long
    #define P 1000000000000000ll
    #define INF 999999999999999ll
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
    	return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
    	RE int x=0,f=1;char ch=getc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    	return x*f;
    }
    const int MAXN=2010,maxn=2000000;
    int n,m,maxx,top;
    int p[MAXN],v[MAXN],c[MAXN];
    inline void prepare()
    {
    	rep(2,maxx,i)
    	{
    		if(!v[i])v[i]=p[++top]=i;
    		rep(1,top,j)
    		{
    			if(maxx/i<p[j]||v[i]<p[j])break;
    			v[i*p[j]]=p[j];
    		}
    	}
    }
    struct wy
    {
    	int len;
    	ll a[MAXN];
    	wy(){memset(a,0,sizeof(a));len=0;}
    	void mul(int x)
    	{
    		ll res=0;
    		rep(0,len,i)
    		{
    			a[i]=a[i]*x;
    			a[i]=a[i]+res;
    			res=a[i]/P;
    			a[i]=a[i]%P;
    		}
    		if(res)a[++len]=res;
    	}
    	inline void print()
    	{
    		printf("%lld",a[len]);
    		fep(len-1,0,i)printf("%015lld",a[i]);
    	}
    	inline wy friend operator -(wy x,wy y)
    	{
    		wy w;w.len=x.len;
    		fep(x.len,0,i)
    		{
    			w.a[i]=x.a[i]-y.a[i];
    			if(w.a[i]<0)
    			{
    				int j=i+1;
    				while(!w.a[j])++j;
    				--w.a[j];w.a[i]+=P;
    				fep(j-1,i+1,k)w.a[k]+=INF;
    			}
    		}
    		while(!w.a[w.len]&&w.len)--w.len;
    		return w;
    	}
    }A,B;
    inline void fj(int x,int y)
    {
    	for(int i=1;p[i]*p[i]<=x;++i)
    		while(x%p[i]==0)
    		{
    			x/=p[i];
    			c[p[i]]+=y;
    		}
    	if(x>1)c[x]+=y;
    }
    int main()
    {
    	//freopen("1.in","r",stdin);
    	get(n);get(m);maxx=2000;prepare();
    	if(m>n+3){puts("0");return 0;}
    	rep(2,n+2,i)fj(i,2);fj(n+3,1);
    	rep(2,n+3-m,i)fj(i,-1);
    	A.a[0]=1;B.a[0]=1;
    	rep(1,maxx,i)while(c[i])A.mul(i),--c[i];
    	//cout<<A.a[A.len]<<endl;
    	//A.print();
    	if(m>n+2){A.print();return 0;}
    	rep(2,n+1,i)fj(i,2);fj(n+2,1);fj(2,1);
    	rep(2,n+2-m,i)fj(i,-1);
    	rep(1,maxx,i)while(c[i])B.mul(i),--c[i];
    	A=A-B;A.print();return 0;
    }
    

    关于压位的一个坑点 输出的时候 格式为先输出最后一个 然后剩下的利用printf("%xmd")的格式 原本%md的意思是指不够m位左边补空格 xmd 表示左边补x.

    注意此时x为0.还有做减法的时候要注意判断len是否等于0 不然数组下标容易越界。

  • 相关阅读:
    stack, deque 和 queue的对比
    Android 修改圆形progressBar颜色
    java.lang.OutOfMemoryError: Failed to allocate a 3110419 byte allocation with 741152 free bytes and
    Android GreenDAO 3.0 不修改版本号的情况下增加、删除表、添加字段
    Android监听安装卸载
    Android实现异步的几种方法
    Android GreenDao清空数据库的方法
    Android 6.0 动态权限申请
    Android无需权限显示悬浮窗
    Android 极光推送JPush---自定义提示音
  • 原文地址:https://www.cnblogs.com/chdy/p/12585287.html
Copyright © 2011-2022 走看看