思路:对于n个点的完全图,有C(n,4)个,我们需要先枚举第一个小于k的完全图(但是需要和70取最小值,否则剩下的点可能构造剩下的数不够),然后还剩下k-C(n,4)个,我们每添加一个点,向完全图连接n2条边, 会增加C(n2,3)个,我们可以用背包dp这个值,使得使用的点最少。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int f[100][5];
int dp[N], choice[N];
void init(){
for(int i=3; i<=75; i++){
f[i][3]=i*(i-1)*(i-2)/6;
if(i>3)
f[i][4]=i*(i-1)*(i-2)*(i-3)/24;
}
}
int k;
int main(){
init();
scanf("%d", &k);
int n1=4, n2=0;
while(f[n1][4]<=k)n1++;
n1--;
n1=min(n1, 70);
k-=f[n1][4];
for(int i=1; i<=k; i++) dp[i]=0x3f3f3f3f;
dp[0]=0;
for(int i=3; i<=n1; i++){
for(int j=f[i][3]; j<=k; j++){
if(dp[j]>dp[j-f[i][3]]+1){
dp[j]=dp[j-f[i][3]]+1;
choice[j]=i;
}
}
}
int e=n1*(n1-1)/2;
int tmp=k;
while(tmp){
e+=choice[tmp];
n2++;
tmp-=f[choice[tmp]][3];
}
printf("%d %d\n", n1+n2, e);
for(int i=1; i<=n1; i++){
for(int j=i+1; j<=n1; j++)
printf("%d %d\n", i, j);
}
for(int i=n1+1; i<=n1+n2; i++){
for(int j=1; j<=choice[k]; j++)
printf("%d %d\n", i, j);
k-=f[choice[k]][3];
}
return 0;
}