2418. 光之大陆

题目链接

2418. 光之大陆

在光之大陆的土地上,各种势力盘根错节。

来自光之峡谷的精灵,来自黑暗森林的亡灵,来自古老东方的人类共同生活在一起。

善于打造装置的矮人,善于发明的侏儒,隐匿于山林的巨人也坚守着属于自己的领土。

这些种族之间关系错综复杂,构成了极其庞大的关系网络。

大魔法师小 P 想要研究其中的种族关系。

两个物种之间可以是盟友,也可以不是盟友,如果 a1,a2..an 满足 aiai+1 是盟友,且 ana1 是盟友,则他们构成了一个联盟。

由于光之大陆正处于微妙的和平之中。所以一个合理的物种关系应满足如下条件:

  1. 对于任意两个物种 A,B,都存在一个序列 A,a1,a2..an,B,使得任意相邻两个种族是盟友(注意 A,B 不一定是盟友)。
  2. 对于任意两个联盟 Sa,Sb,都不存在一个物种既参加了联盟 Sa,又参加了联盟 Sb

P 想知道,大陆上的 N 个种族一共有多少种可能的结盟关系,由于结果可能很大,你只需要输出答案 modM 的值。

输入格式

一行,两个正整数 N,M

输出格式

一个整数,表示方案数 modM 的值。

数据范围

3N200,
1M106

输入样例:

4 1000000

输出样例:

31

解题思路

prufer编码

问题即给出一个 n 个点的无向完全图,问有多少个不同的点仙人掌图
将该问题拆解:先求出 n 个点分成 m 个环的方案,然后再将这些环组成生成树的方案数,对于第二步,考虑prufer编码,每个点的父亲节点的范围为 1n,且由于是完全图,不难发现,对于任意一个点的父亲节点,对于 1n 的任何一个点作为其父亲节点的概率都是相等的,即prufer编码有 nm2 种,即将环组成生成树的方案有 nm2 种,再来考虑第一步,考虑 dpf[i][j] 表示前 i 个点分成 j 个环的方案数,枚举 1 号所在环的环数,这样的环有 Ci1k1 种,环也有不同的种类,k 点排列有 k!,假定我们每次都是拿环上最上面的那个点连边,则显然有 k 种方案,即对应全排列,可以看成旋转操作形成的环是不同的,但是由于是无向图,对于某条边来说会连两次,则这部分的转移方程:f[i][j]=k=1ij+1f[ik][j1]×Ci1k1×k!2,则答案为 k=1nf[n][k]×nk2,观察式子,不难发现 k=1 时会出问题,对于含 n 个点的环,这时显然不用向外连边,即这时的总方案应该为 n12

  • 时间复杂度:O(n3)

代码

// Problem: 光之大陆 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/2420/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=205; int n,m,C[N][N],g[N],f[N][N]; void init() { C[0][0]=1; for(int i=0;i<=n;i++) for(int j=0;j<=i;j++) { if(!j)C[i][j]=1; else C[i][j]=(C[i-1][j-1]+C[i-1][j])%m; } g[1]=1%m,g[3]=3%m; for(int i=4;i<=n;i++)g[i]=(LL)g[i-1]*i%m; } int main() { scanf("%d%d",&n,&m); init(); f[0][0]=1; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) for(int k=1;k<=i-j+1;k++) f[i][j]=(f[i][j]+(LL)f[i-k][j-1]*C[i-1][k-1]*g[k]%m)%m; int res=g[n-1],p=1; for(int i=2;i<=n;i++) { res=(res+(LL)f[n][i]*p)%m; p=(LL)p*n%m; } printf("%d",res); return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16952572.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(77)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示