2418. 光之大陆

题目链接

2418. 光之大陆

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

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

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

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

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

两个物种之间可以是盟友,也可以不是盟友,如果 \(a_1,a_2..a_n\) 满足 \(a_i\)\(a_{i+1}\) 是盟友,且 \(a_n\)\(a_1\) 是盟友,则他们构成了一个联盟。

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

  1. 对于任意两个物种 \(A,B\),都存在一个序列 \(A,a_1,a_2..a_n,B\),使得任意相邻两个种族是盟友(注意 \(A,B\) 不一定是盟友)。
  2. 对于任意两个联盟 \(S_a,S_b\),都不存在一个物种既参加了联盟 \(S_a\),又参加了联盟 \(S_b\)

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

输入格式

一行,两个正整数 \(N,M\)

输出格式

一个整数,表示方案数 \(\bmod M\) 的值。

数据范围

\(3 \le N \le 200\),
\(1 \le M \le 10^6\)

输入样例:

4 1000000

输出样例:

31

解题思路

prufer编码

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

  • 时间复杂度:\(O(n^3)\)

代码

// 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;
}
posted @ 2022-12-05 16:01  zyy2001  阅读(47)  评论(0编辑  收藏  举报