题意:
用最多75个点输出一个图,要求图中有刚好k个四元完全图(两两相连)
解析:
n个点的完全图,任选4个就可以算一个->C(n,4)
在这个图的基础上,有另外一个点m,从n个点中选出q个点,那么k个点中任意选3个加上m就是一个->C(q,3)
要做的就是得到一个n和多个q使其的贡献加起来为k
刚开始的时候我用贪心做,没有考虑贪心的时候点的个数会超过75
得出的结论是空出5个点进行C(n,3~n)中的选择,就可以填满k和C(n,4)的差值
(暴力n^4枚举前4个判断最后一个知否可以补满也是可以做的…)
所以5个点都有n-2种选择,且每个点只能用一次,相当于5个背包,每个背包中有n-2个数,每个背包只能选出一个数
代码:
D read(){ D ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
const int N=1000;
const F pi=acos(-1);
const D mod=1e9+7;
const F _e=2.718281828459045;
/*快速幂*/
D swift(D a,D b){
D ans=1ll;
while(b){
if(b%2)ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}return ans;
}
D inv(D a){return swift(a,mod-2);}//费马小定理
D fac[N+9];
D inv_fac[N+9];
void init_fac(){
fac[0]=fac[1]=1ll;
for(int i=2;i<=N;i++)fac[i]=fac[i-1]*i%mod;
//预处理阶乘逆元
inv_fac[N]=swift(fac[N],mod-2);//费马小定理求 N!的逆元
for(D i=N-1;i>=1;i--) inv_fac[i]=(inv_fac[i+1]*(i+1))%mod;
inv_fac[0]=1;
}
D C(D a,D b){//组合数 a下
if(b>a||b<0)return 0;
return fac[a]*inv_fac[a-b]%mod*inv_fac[b]%mod;
}
D dp[100000];
int path[100000];
int main(){
init_fac();
// cout<<C(70,4);//916895
D k=read();
D n=4ll;
while(n<70&&C(n+1,4)<=k)n++;
if(C(n,4)==k){
cout<<n<<' '<<n*(n-1)/2<<'\n';
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++){
printf("%d %d\n",i,j);
}
}
return 0;
}
k-=C(n,4);
mmm(dp,0);dp[0]=1;
for(int i=1;i<=5;i++){//枚举背包
for(int g=k;g>=0;g--){//枚举容量
for(int j=3;j<=n;j++){//枚举物品
if(g-C(j,3)>=0&&dp[g-C(j,3)]){dp[g]=1;if(path[g]==0)path[g]=j;}//}
}
}
}
int ansp=n,anse=n*(n-1)/2;
int now=k;
while(1){
ansp++;anse+=path[now];
now-=C(path[now],3);
if(now<=0)break;
}
cout<<ansp<<' '<<anse<<'\n';
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++){
printf("%d %d\n",i,j);
}
}
now=k;int po=n;
while(1){
po++;
for(int i=1;i<=path[now];i++)printf("%d %d\n",i,po);
now-=C(path[now],3);
if(now<=0)break;
}
}