Counting 4-Cliques(分组背包加路径)

原题:牛客网暑期ACM多校训练营(第七场)

题意:
这里写图片描述
用最多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;
    }

}


猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/81567213
今日推荐