jzoj5983. 【北大2019冬令营模拟2019.1.1】多边形 (组合数学)

这其实是道打表题……你看我代码就知道了……

咳咳来点严谨证明好了……

前方高能请注意

首先,正多边形近似于圆,可以看做在圆里内接多边形。圆内接多边形最多只有三个锐角。因为凸多边形的外角和为\(360\)度,如果有大于等于\(4\)个锐角,那么有大于等于\(4\)个外角大于\(90\)度,外角和肯定大于\(360\)度,矛盾(话说我当时只猜想出了结论不知道怎么证明……初中数学全还给老师了→_→)

那么分情况讨论\(k=0,1,2,3\)的情况就好了。顺便注意\(n\)为奇数,所以不可能存在直角的情况

k=3

此时,这个多边形肯定是三角形,所以如果\(m\neq 3\)无解。(因为我证不来所以就当做是显然好了)所以这里可以容斥,用总的三角形个数减去钝角三角形的个数

对于钝角三角形,三个点肯定在直径的同一侧,我们可以枚举两个点,然后第三个点肯定在这两个点在圆上较短的那条圆弧上,且上面任意一个点与这两个点组成的三角形都是钝角

从下图中不难看出,直径一边的点数最多为\(\left\lfloor\frac{n}{2}\right\rfloor+1\)

于是我们可以枚举每一个点然后逆时针枚举第二个点,那么当第二个点每逆时针走一格,第三个点能选的位置也相对应增加一个。而第三个点最多能选的位置是\(\left\lfloor\frac{n}{2}\right\rfloor-1\)(减去位于两端的第一第二个点),那么对于这一个点它能形成的钝角三角形就是\(\sum_{i=1}^{\left\lfloor\frac{n}{2}\right\rfloor-1}i\),用等差数列求和公式求和,然后再乘上\(n\)就是总的钝角三角形个数,用总的三角形个数减去它就是锐角三角形个数了

k=2

首先,如果一个角是锐角,那么与这个点\(i\)相邻的两个点,他们不经过\(i\)的那段圆弧必定小于周长的一半。其次,不难发现如果\(k=2\),那么两个锐角相对应的点必定在多边形上相邻

于是我们可以枚举锐角相对应的点,设为\(A,B\),如果下图所示,剩下的点只有落在紫色区域才能使\(A,B\)都是锐角且不存在第三个锐角(如果\(m>3\)都行,如果\(m=3\)只能在上面那块紫色区域)

于是照例枚举\(A\)并逆时针枚举\(B\),设\(AB\)之间有\(k\)个点(不包括\(A,B\)),即上面那个紫色区域里可选的点有\(k\)个,易知下面那块紫色区域可选的点为\(k+1\)个(可选的点都是在正\(n\)边形上的)。上面那块紫色区域,\(k\)最大为\(\left\lfloor\frac{n}{2}\right\rfloor-1\),于是每一个\(A\)上面那块紫色区域的贡献就是\(\sum_{i=1}^{\left\lfloor\frac{n}{2}\right\rfloor-1}C_{i}^{m-2}=C_{\left\lfloor\frac{n}{2}\right\rfloor}^{m-1}\)。如果\(m\neq 3\)就把下面那一块的贡献也加上去。最后将贡献乘上\(n\)就是每一个点的贡献了

这里顺便说一下形如\(\sum_{i=1}^nC_{i}^m\)的东西怎么求和。把它拆出来,前面加上\(C_{0}^m\)\(C_{0}^{m+1}\)(这两个都等于\(0\))。于是原式为\(C_0^{m+1}+C_0^m+C_{1}^m+C_2^m+...+C_n^m=C_1^{m+1}+C_1^m+...\),然后不断合并前两项,得到\(C_{n+1}^{m+1}\)

k=1

这种情况很麻烦,我们要保证除了枚举的点其它都不是锐角。可以按下图所示的方案来,枚举锐角\(A\),设\(E,F\)为剩下所有点的两个边界,那么\(EF\)不经过\(A\)的那段圆弧的长必定小于圆周长的一半。我们要保证\(E,F\)所对的两个角不是锐角,设\(C,D\)为与\(E,F\)相邻的第一个点,只有在\(C\)\(E\)\(F\)\(D\)都在如图直径的同一侧时,才能保证\(E,F\)都是钝角

于是我们可以枚举\(C,D\),则\(C,D\)必在这条直径的两侧,我们可以枚举\(C,D\)之间的点数\(k\)(不包含\(C,D\)),设\(T=\left\lfloor\frac{n}{2}\right\rfloor+1\)(即直径的一边最多能有的点数),则\(k\)的上界为\(T-4\),对于每一个\(k\),考虑\(C,D\)的放法,共有\(k+1\)种,对于每一种\(C,D\)的放法,因为\(E,F\)之间包含\(E,F\)的点数最多为\(T\),所以当\(E\)逆时针移动时,\(F\)能放的位置也逆时针移动,且每次增加\(1\),当\(E,C\)在正\(n\)边形上相邻时,\(F\)能放的位置为\(T-k-3\),于是\(E,F\)放置的方案数为\(\sum_{i=1}^{T-k-3}i\)(先别用等差数列求和公式化掉)

综上,对于每一个作为锐角的点,可行的方案数为\[Ans=\sum_{k=0}^{T-4}C_{k}^{m-5}(k+1)\sum_{i=1}^{T-k-3}i\]
然后答案乘上个\(n\)就是总的方案数。顺便从上式看出如果\(m<5\)无解

然而上式的计算是\(O(n)\)的,太慢

考虑变换求和顺序,有\[Ans=\sum_{i=1}^{T-3}i\sum_{k=0}^{T-i-3}C_{k}^{m-5}(k+1)\]
考虑形如\(\sum_{i=0}^n (i+1)C_i^m\)的东西,有\[\sum_{i=0}^n (i+1)C_i^m=\sum_{i=0}^n\frac{(i+1)!}{(m+1)!(m-i)!}(m+1)\]
则这玩意儿等于\((m+1)\sum_{i=0}^nC_{i+1}^{m+1}\),再按照\(k=2\)那个时候最下面说的化一下,又等于\((m+1)C_{n+2}^{m+2}\)

于是\[\sum_{k=0}^{T-i-3}C_{k}^{m-5}(k+1)=(m-4)C_{T-i-1}^{m-3}\]

原式变为\[Ans=(m-4)\sum_{i=1}^{T-3}i\times C_{T-i-1}^{m-3}\]

\(T-i-1<m-3\)时组合数为\(0\),所以可以写成\[Ans=(m-4)\sum_{i=1}^{T-m+2}i\times C_{T-i-1}^{m-3}\]
考虑后面那一坨东西,令\(M=m-3\),展开之后为\(C_{T-2}^M+2C_{T-3}^M+...+(T-M-1)C_M^M\)
我们可以在后面加上一堆\(C_{i}^M(i<M)\)反正都等于\(0\),于是上式可以化为\(C_{T-1}^{M+1}+C_{T-2}^{M+1}+...+C_{M+1}^{M+1}=C_{T}^{M+2}\)

综上\(k=1\)时每一个点的答案为\(Ans=(m-4)C_T^{m-1}\)

k=0

只要用所有的情况减去锐角分别为\(1,2,3\)的情况就好了

//minamoto
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#define R register
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int K=-1,Z=0;
inline void Ot(){fwrite(sr,1,K+1,stdout),K=-1;}
void print(R int x){
    if(K>1<<20)Ot();if(x<0)sr[++K]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++K]=z[Z],--Z);sr[++K]='\n';
}
const int N=1e6,P=1000109107,inv2=500054554;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
    return res;
}
int fac[N+5],inv[N+5];
int n,m,k,res;
inline int calc(R int n){return 1ll*n*(n/2)%P*(n/2-1)%P*inv2%P;}
inline int C(R int n,R int m){if(m>n)return 0;return 1ll*fac[n]*inv[m]%P*inv[n-m]%P;}
void init(){
    inv[0]=fac[0]=1;fp(i,1,N)fac[i]=mul(fac[i-1],i);
    inv[N]=ksm(fac[N],P-2);fd(i,N-1,1)inv[i]=mul(inv[i+1],i+1);
}
int calc3(){
    if(m!=3)return 0;
    int res=C(n,3);
    res=dec(res,calc(n));
    return res;
}
int calc2(){
    int res=C(n/2,m-1);
    if(m!=3)res=add(res,C(n/2+1,m-1));
    res=mul(res,n);
    return res;
}
int calc1(){
    if(m<5)return 0;
    int res=0,T=n/2+1;
    res=C(T,m-1);
    res=mul(res,m-4);
    return mul(res,n);
}
int main(){
//  freopen("testdata.in","r",stdin);
    freopen("polygon.in","r",stdin);
    freopen("polygon.out","w",stdout);
    int T=read();init();
    while(T--){
        n=read(),m=read(),k=read();
        switch(k){
            case 3:res=calc3();break;
            case 2:res=calc2();break;
            case 1:res=calc1();break;
            case 0:{
                res=C(n,m);
                res=dec(res,calc1()),res=dec(res,calc2()),res=dec(res,calc3());
                break;
            }
            default:res=0;break;
        }print(res);
    }return Ot(),0;
}

猜你喜欢

转载自www.cnblogs.com/bztMinamoto/p/10214789.html
今日推荐