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

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

前方高能请注意

首先,正多边形近似于圆,可以看做在圆里内接多边形。圆内接多边形最多只有三个锐角。因为凸多边形的外角和为\(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_im=\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;
}

jzoj5983. 【北大2019冬令营模拟2019.1.1】多边形 (组合数学)的更多相关文章

  1. jzoj5990. 【北大2019冬令营模拟2019.1.6】Bear (状压dp)

    题面 题解 我永远讨厌dp.jpg 搞了一个下午优化复杂度最后发现只要有一个小trick就可以A了→_→.全场都插头dp就我一个状压跑得贼慢-- 不难发现我们可以状压,对于每一行,用状态\(S\)表示 ...

  2. jzoj5991. 【北大2019冬令营模拟2019.1.6】Juice

    题面 题解 好迷-- //minamoto #include<bits/stdc++.h> #define R register #define ll long long #define ...

  3. jzoj5989. 【北大2019冬令营模拟2019.1.6】Forest (set)

    题面 题解 为了一点小细节卡了一个下午--我都怕我瞎用set把电脑搞炸-- 观察一次\(1\)操作会造成什么影响,比如说把\(A[i]\)从\(x\)改成\(y\): \(D[x]\)会\(-1\), ...

  4. jzoj5984. 【北大2019冬令营模拟2019.1.1】仙人掌 (分块)

    题面 题解 数据结构做傻了.jpg 考虑每一个节点,它的儿子的取值最多只有\(O(\sqrt {m})\)种,那么可以用一个双向链表维护儿子的所有取值以及该取值的个数,那么对儿子节点修改一个值就是\( ...

  5. JZOJ[5971]【北大2019冬令营模拟12.1】 party(1s,256MB)

    题目 题目大意 给你一棵树,在树上的某一些节点上面有人,要用最小的步数和,使得这些人靠在一起.所谓靠在一起,即是任意两个人之间的路径上没有空的节点(也就是连在一起). N≤200N \leq 200N ...

  6. [JZOJ5977] 【清华2019冬令营模拟12.15】堆

    题目 其中n,q≤500000n,q\leq 500000n,q≤500000 题目大意 让你维护一个堆.支持一下操作: 在某个点的下面加上另一个点,然后进行上浮操作. 询问某一点的权值. 思考历程 ...

  7. Visual Studio 2019 发布活动 - 2019 年 4 月 2 日

    Visual Studio 2019 发布活动 2019 年 4 月 2 日,星期二 | 上午 9:00 (PT) 围观: https://visualstudio.microsoft.com/zh- ...

  8. [2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania

    [2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见&quo ...

  9. jzoj6101. 【GDOI2019模拟2019.4.2】Path

    题目链接:https://jzoj.net/senior/#main/show/6101 记\(f_i\)为从\(i\)号点走到\(n\)号点所花天数的期望 那么根据\(m\)条边等可能的出现一条和一 ...

随机推荐

  1. 从S3中导入数据到Dynamodb

    本节如果你已经从Dynamodb中导出过数据,而且导出的文件以及被存入S3.文件内部结构会在Verify Data Export File 中描写叙述. 我们称之前导出数据的原始表为source ta ...

  2. jquery根据(遍历)html()的内容/根据子元素的内容(元素文本)来选择(查询),在子元素前加入元素

    <ul> <li>First</li> <li>second</li> <li>third</li> </ul ...

  3. EasyPusher手机直播编码推送之图像旋转90度后画面重复的问题

    本文转自EasyDarwin开源团队开发Holo的博客:http://blog.csdn.net/holo_easydarwin 最初在做EasyPusher手机直播的时候遇到过一个问题:手机竖屏推送 ...

  4. python中的括号以及元组和列表的区别

    1 python中的括号 1.1 花括号 花括号表示的是字典,即键值对. 1.2 方括号 方括号表示的是列表,类似于数组,但是可以允许存放混杂类型的数据. 1.3 圆括号 圆括号表示的是元组,类似于列 ...

  5. Splits a tensor into sub tensors

    https://www.tensorflow.org/api_docs/python/tf/split # 'value' is a tensor with shape [5, 30] # Split ...

  6. NSArray / NSSet / NSDictory 三者的异同点

    NSArray / NSSet / NSDictory 三者的异同点 NSArray 是一个有序对象的一个集合.相当于一个队列存储,可以有重复的数进去. NSSet 比较典型的一个HASH表(集合)算 ...

  7. spring boot 整合 quartz 集群环境 实现 动态定时任务配置【原】

    最近做了一个spring boot 整合 quartz  实现 动态定时任务配置,在集群环境下运行的 任务.能够对定时任务,动态的进行增删改查,界面效果图如下: 1. 在项目中引入jar 2. 将需要 ...

  8. LightOJ - 1104 Birthday Paradox —— 概率

    题目链接:https://vjudge.net/problem/LightOJ-1104 1104 - Birthday Paradox    PDF (English) Statistics For ...

  9. POJ2389 —— 高精度乘法

    直接上代码了: #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib& ...

  10. Nginx 基本介绍

    同类产品 同类竞争产品,Apache,Tomcat,IIS等. Tomcat面向Java. IIS只能在Windows上运行. Apache有很多优点,稳定,开源,跨平台.但是它比较重,而且不支持高并 ...