NOI.AC 31 MST——整数划分相关的图论(生成树、哈希)[通俗易懂]

NOI.AC 31 MST——整数划分相关的图论(生成树、哈希)[通俗易懂]NOI.AC 31 MST——整数划分相关的图论(生成树、哈希)

大家好,又见面了,我是你们的朋友全栈君。

题目:http://noi.ac/problem/31

模拟 kruscal 的建最小生成树的过程,我们应该把树边一条一条加进去;在加下一条之前先把权值在这一条到下一条的之间的那些边都连上。连的时候要保证图的连通性不变。

已经加了一些树边之后,图的连通性是怎样的呢?这可以是一个整数划分的问题。据说方案只有4万多,所以可以搜一下,搜出有 k 个连通块的方案数。

为了转移和转移时算方案数,还要记录每个方案的:各个连通块的点数,所有的空位(可放边)数。

可以用 map 来存状态。 map 的角标是一个随便哈希的值,map 的值是这个状态的编号,也是这个状态的其他信息在那些数组里的角标。这样要算下一个状态是谁的时候就可以通过记录的“各个连通块的点数”找到”下一个状态的各个连通块的点数“,再用一样的方法哈希起来,利用 map 就能找到下一个状态的编号了。

因为不太会写,所以就学习(抄)了一下别人的。

1.注意算排列时判 n<m !数组越界本地可能答案正确,但交上去就会爆。

2.不知 1e4 是怎么确定的?

3.学题解 N=41 WA了最后两个点,改成45就A了。不知为何。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define ull unsigned long long
#define ll long long
using namespace std;
const int N=45,M=1e4,mod=1e9+7,base=M;//N=41会WA两个点?!
int n,a[N],cd[N],vec[N][M][N],edg[N][M];//N的情况里第M个的 空位/第N个连通块的点数
int dp[N][M],lm,tmp[N],id[N][N],tmpx[N],top;
int jc[N*N],jcn[N*N];
ull hsh;
map<ull,int> mp[N];//用值得到另一个角标(cd)
int rdn()
{
    int ret=0;bool fx=1; char ch=getchar();
    while(ch>'9'||ch<'0'){
   
   if(ch=='-')fx=0; ch=getchar();}
    while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
    return fx?ret:-ret;
}
int pw(int x,int k)
{
    int ret=1;while(k){
   
   if(k&1ll)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1ll;}return ret;
}
void init()
{
    lm=(n*(n-1)>>1);//not n
    jc[0]=1;
    for(int i=1;i<=lm;i++) jc[i]=(ll)jc[i-1]*i%mod;
    jcn[lm]=pw(jc[lm],mod-2);
    for(int i=lm-1;i>=0;i--) jcn[i]=(ll)jcn[i+1]*(i+1)%mod;
}
void dfs(int sm,int cr,int lst)
{
    if(lst*(lm-cr+1)>sm) return;
    if(cr>lm)
    {
        hsh=0;
        for(int i=1;i<cr;i++)hsh=hsh*base+tmp[i];
        mp[lm][hsh]=++cd[lm];
        for(int i=1;i<cr;i++)
        {
            vec[lm][cd[lm]][i]=tmp[i];
            edg[lm][cd[lm]]+=(tmp[i]*(tmp[i]-1)>>1);
        }
        //printf("edg[%d][%d]=%d\n",lm,cd[lm],edg[lm][cd[lm]]);
        return;
    }
    if(cr==lm)
    {
        tmp[cr]=sm;dfs(0,cr+1,0);//有剪枝,所以一定不降
        return;
    }
    for(int i=lst;i<=sm;i++)
    {
        tmp[cr]=i;
        dfs(sm-i,cr+1,i);
    }
}
int P(int n,int m)
{
    if(n<m) return 0;//!!!!!!
    //printf("jc[%d]=%d jcn[%d]=%d\n",n,jc[n],n-m,jcn[n-m]);
    return (ll)jc[n]*jcn[n-m]%mod;
}
int main()
{
    n=rdn();
    init();
    for(int i=n;i>1;i--) a[i]=rdn(); a[1]=(n*(n-1)>>1);
    for(int i=1;i<=n;i++) lm=i,dfs(n,1,1);
    dp[n][1]=1;
    for(int i=n;i>1;i--)
        for(int s=1;s<=cd[i];s++)
        {
            //printf("y dp[%d][%d]=%d\n",i,s,dp[i][s]);
            //printf("%d-%d=%d %d-%d-1=%d\n",edg[i][s],a[i+1],edg[i][s]-a[i+1],a[i],a[i+1],a[i]-a[i+1]-1);
            dp[i][s]=(ll)dp[i][s]*P(edg[i][s]-a[i+1],a[i]-a[i+1]-1)%mod;
            //printf("now dp[%d][%d]=%d(P=%d)\n",i,s,dp[i][s],P(edg[i][s]-a[i+1],a[i]-a[i+1]-1));
            memset(id,0,sizeof id);
            for(int j=1;j<=i;j++)tmp[j]=vec[i][s][j];
            for(int u=1;u<=i;u++)
                for(int v=u+1;v<=i;v++)//哪两个集合
                {
                    //printf("i=%d s=%d tmp[%d]=%d tmp[%d]=%d\n",i,s,u,tmp[u],v,tmp[v]);
                    if(!id[tmp[u]][tmp[v]])
                    {
                        top=0;
                        for(int p=1;p<=i;p++)
                            if(p!=u&&p!=v)tmpx[++top]=tmp[p];
                        tmpx[++top]=tmp[u]+tmp[v];
                        for(int p=top-1;p;p--)//插排
                            if(tmpx[p]>tmpx[p+1])swap(tmpx[p],tmpx[p+1]);
                            else break;//else
                        hsh=0;
                        for(int p=1;p<=top;p++)
                            hsh=hsh*base+tmpx[p];
                        id[tmp[u]][tmp[v]]=mp[i-1][hsh];
                        //printf("id[%d][%d]=%d\n",tmp[u],tmp[v],id[tmp[u]][tmp[v]]);
                    }
                    dp[i-1][id[tmp[u]][tmp[v]]]=
                        (dp[i-1][id[tmp[u]][tmp[v]]]+(ll)dp[i][s]*tmp[u]*tmp[v])%mod;
                    //printf("dp[%d][%d]=%d(dp=%d tmu=%d tmv=%d)\n",i-1,id[tmp[u]][tmp[v]],dp[i-1][id[tmp[u]][tmp[v]]],dp[i][s],tmp[u],tmp[v]);
                }
        }
    //printf("%d-%d=%d %d-%d-1=%d\n",edg[1][1],a[2],edg[1][1]-a[2],a[1],a[2],a[1]-a[2]-1);
    dp[1][1]=(ll)dp[1][1]*P(edg[1][1]-a[2],a[1]-a[2]-1)%mod;
    printf("%d\n",dp[1][1]);
    return 0;
}

 

转载于:https://www.cnblogs.com/Narh/p/9675064.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/107298.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 发展,需求驱动 &#183; 一间 所见即所得

    发展,需求驱动 &#183; 一间 所见即所得

    2022年1月3日
    45
  • 期货真的可以做到长期稳定盈利吗?[通俗易懂]

    期货真的可以做到长期稳定盈利吗?[通俗易懂]我主要做外汇,期货和期权,A股也做,但是中国的股票你们知道的,做空的限制太多,融券融不到,股指期货还限制开仓和提高杠杆率。所以要等一个轮回需要5年以上,所以股票等待建仓机会比较漫长。从交易者的层面来看,我一般把他们分为这么几类人:一,幼儿园阶段:无知者无畏这种人没做过交易,只是从朋友那里听说,交易能赚大钱,或者是书刊杂志上读了一些交易大师的成功学传记,然后就跟打了鸡血似的,觉得自己也能和他们一样在金融市场赚到很多钱,这些人没有风控意识,甚至感觉这个市场只会赚钱,不会亏钱。于是他们就开户,然后一头

    2022年10月5日
    3
  • 实验室设备管理系统[通俗易懂]

    实验室设备管理系统[通俗易懂]#include<stdio.h>#include<stdlib.h>#include<string.h>#defineMAX_NUM100 //数组最大长度typedefstruct_EQUIPMENT{intnum;//编号intis_bolish;//是否报废charstyle[20];//种类c…

    2022年10月13日
    4
  • Django Django_django xadmin

    Django Django_django xadmin前言我们通常做查询操作的时候,都是通过模型名字.objects的方式进行操作。其实模型名字.objects是一个django.db.models.manager.Manager对象,而Manager

    2022年7月29日
    4
  • 自动编码器及其变种

    自动编码器及其变种自动编码器  三层网络结构:输入层,编码层(隐藏层),解码层。  训练结束后,网络可由两部分组成:1)输入层和中间层,用这个网络对信号进行压缩;2)中间层和输出层,用这个网络对压缩的信号进行还原。图像匹配就可以分别使用,首先将图片库使用第一部分网络得到降维后的向量,再讲自己的图片降维后与库向量进行匹配,找出向量距离最近的一张或几张图片,直接输出或还原为原图像再匹配。  该网络的目的是重…

    2022年5月20日
    40
  • Lambda plus: 云上大数据解决方案

    Lambda plus: 云上大数据解决方案本文会简述大数据分析场景需要解决的技术挑战,讨论目前主流大数据架构模式及其发展。最后我们将介绍如何结合云上存储、计算组件,实现更优的通用大数据架构模式,以及该模式可以涵盖的典型数据处理场景。大数据处理的挑战现在已经有越来越多的行业和技术领域需求大数据分析系统,例如金融行业需要使用大数据系统结合VaR(valueatrisk)或者机器学习方案进行信贷风控,零售、餐饮行业需要大数据系统…

    2022年6月2日
    30

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号