P7529 [USACO21OPEN] Permutation G 题解
G组练习总结#10
好喜欢妙妙题!
stO Benq Orz
题目大意
Bessie 喜欢画画,现在它给你了
Bessie 会先选择一个这
首先,她会将
然后对于
Bessie 会一直这样操作下去,她限制我们,对于所有
现在她请我们求出所有合法的排列
题目保证没有任何三个点共线。
解题思路
第一个操作会给我们的图一个初始的大三角形,这没什么好说的。
我们可以试着分析加入
目前状况是这样的:
我们分别讨论
当然
在这种情况里,
如果是这种情况,
画出多种情况后,我们很容易看出来,所有合法的情况里,都有一个最大的三角形包含了所有的点。
此外,整个大三角形内部会被分割为一个个三角形。
对于所有的
否则,判断能不能形成新的大三角形包含所有点,可以,就和大三角形的三个点连边,不行,则为不合法的情况。
具体实现
我们先考虑最重要的操作之一,如何判断一个点是否在三角形内?
对于这个问题,我们可以判断新连边后切割出来的新的三角形的面积之和与原本三角形面积的大小关系,只有相等时点在三角形内。
三角形面积用向量处理,公式很简单:
同时我们要用
我们设
计算答案我们可以考虑倒序处理这个问题,因为,我们发现,最终的所有点都应该被包含在一个最大的三角形里,否则答案为 0!
那我们记录下所有的三角形三元组,按照
初值只需要找到第一个三元组
现在我们考虑转移:
设现在的三角形是
首先,
先给式子:
其中我们定义
解释一下式子,我们现在要将
这新的
对于
最后,对于一个
式子:
怎么说?我们定
记得取模!时间复杂度
完结撒花(●'◡'●)
参考代码
#include<bits/stdc++.h>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
typedef long long ll;
using namespace std;
const int N=51,d=1e9+7;
int n,dp[N][N][N],a[N],b[N],w[N][N][N],f,x,y,z,s[N]={1},t[N],cnt,ans,o[5];
struct tri
{
int a,b,c;
}tr[N*N*N];
int calc(int i,int j,int k)//计算面积
{
int v=abs(a[i]*(b[j]-b[k])+a[j]*(b[k]-b[i])+a[k]*(b[i]-b[j]));
return v;
}
int check(int i,int j,int k,int l)//判断是否在三角新内
{
if(calc(i,j,l)+calc(i,k,l)+calc(j,k,l)==calc(i,j,k))
return 1;
return 0;
}
int quickpow(int b,int p)
{
int w=1;
while(p)
{
if(p&1)
w=1ll*w*b%d;
b=1ll*b*b%d;
p=p>>1;
}
return w;
}
int P(int x,int y)//排列方案数
{
int v=1ll*s[x]*t[x-y]%d;
return v;
}
bool cmp(tri x,tri y)
{
return w[x.a][x.b][x.c]<w[y.a][y.b][y.c];
}
int main()
{
int i,j,k,l;
scanf("%d",&n);
for(i=1;i<=n;++i)
scanf("%d%d",a+i,b+i),s[i]=1ll*s[i-1]*i%d;
t[n]=quickpow(s[n],d-2);
for(i=n;i;--i)
t[i-1]=1ll*t[i]*i%d;
for(i=1;i<=n;++i)//预处理w
{
for(j=i+1;j<=n;++j)
{
for(k=j+1;k<=n;++k)
{
++cnt,tr[cnt]=(tri){i,j,k};
for(l=1;l<=n;++l)
{
if(!check(i,j,k,l))
++w[i][j][k];
}
if(!w[i][j][k]&&!f)
x=i,y=j,z=k,f=1;
}
}
}
if(!f)
{
printf("0");
return 0;
}
sort(tr+1,tr+cnt+1,cmp);//所有
dp[x][y][z]=1;
for(i=1;i<=cnt;++i)
{
x=tr[i].a,y=tr[i].b,z=tr[i].c;
ans=(ans+6ll*P(n-3,n-w[x][y][z]-3)*dp[x][y][z]%d)%d;//算答案
for(j=1;j<=n;++j)
{
if(j!=x&&j!=y&&j!=z&&check(x,y,z,j))
{
for(k=1;k<=3;++k)
{
o[1]=x,o[2]=y,o[3]=z;
o[k]=j;//把新点塞进去
sort(o+1,o+4);//记得要满足大小关系
dp[o[1]][o[2]][o[3]]=(dp[o[1]][o[2]][o[3]]+1ll*P(w[o[1]][o[2]][o[3]]-1,w[o[1]][o[2]][o[3]]-w[x][y][z]-1)*dp[x][y][z]%d)%d;//有点长的转移)
}
}
}
}
printf("%d",ans);
return 0;
}