Description:


\(1<=n<=1e9,1<=m,k<=100\)

模数不是质数。

题解:


先选m个点,最后答案乘上\(C_{n}^m\)。

不妨枚举m个点的度数和D,那么我们需要解决两个问题:

  1. 一共m个有标号盒子,D个有标号小球放到盒子里,且每个盒子的球数不超过k的方案数。
  2. n-m个有标号点的D棵有根树的森林划分

Task1:

事实上这个东西可以直接NTT卷起来,效率应该是最高的,但是因为模数不是质数,所以不行。

设\(f[i][j]\)表示i个盒子,j个小球的方案数。

不难得到一个容斥的转移:

\(f[i][j]=f[i][j-1]*i-f[i-1[j-(k+1)]*i*C_{j-1}^k\)

组合数直接杨辉三角预处理。

复杂度:\(O(m^2k)\)

Task2:

利用扩展Cayley公式:

n个点,m棵树,且1-m的点在不同的树里的方案数:

拓展prufer序列的定义,现在是取出森林中最大的叶子,输出与它相邻的点,删掉它,直到剩下1..m

发现序列长度是n-m,且前n-1-m个位置可以填1..n,最后一个只能填1..m,所以:

\(F(n,m)=m*n^{n-1-m}\)

那直接乘上一个\(C_n^m\)来把1..m换成其它根即是我们要求的。

或者说直接推导:

新建一个虚点n+1,让所有的根连向n+1,那么就可以做树上purfer(取编号最小的叶子),直到剩下n+1一个点。

由于n+1的度数是m,所以在prufer序列中出现m-1次。

因此\(=C_{n-1}^{m-1}*n^{n-m}\)

和上面是等价的。

Task3:

\(Ans=C(n,m)*\sum_{i=0}^{mk}f[m][i]*C_{n-m-1}^{i-1}*(n-m)^{n-m-i}\)

模数不是质数,所以组合数需要分解质因数来算。

Code:


#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i < B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std; int T, n, m, k, mo; ll ksm(ll x, ll y) {
ll s = 1;
for(; y; y /= 2, x = x * x % mo)
if(y & 1) s = s * x % mo;
return s;
} int u[105], v[105], u0;
int pmo; void fen(int x) {
pmo = x;
u0 = 0;
for(int i = 2; i * i <= x; i ++) if(x % i == 0) {
u[++ u0] = i; v[u0] = 0;
for(; x % i == 0; x /= i) v[u0] ++;
}
if(x > 1) u[++ u0] = x, v[u0] = 0;
fo(i, 1, u0) pmo = pmo / u[i] * (u[i] - 1);
} ll c[10005][105]; ll inv(int x) { return ksm(x, pmo - 1);} struct nod {
int v[11];
}; nod operator * (nod a, nod b) {
a.v[0] = (ll) a.v[0] * b.v[0] % mo;
fo(i, 1, u0) a.v[i] += b.v[i];
return a;
}
nod operator / (nod a, nod b) {
a.v[0] = (ll) a.v[0] * inv(b.v[0]) % mo;
fo(i, 1, u0) a.v[i] -= b.v[i];
return a;
} nod p[10005], q[10005]; void gg(int x, nod &p) {
if(!x) {
fo(i, 1, u0) p.v[i] = 0;
p.v[0] = 1;
return;
}
fo(i, 1, u0) {
p.v[i] = 0;
for(; x % u[i] == 0; x /= u[i]) p.v[i] ++;
}
p.v[0] = x;
} void build(int n) {
gg(0, p[0]);
gg(0, q[0]);
fo(i, 1, min(10000, n)) {
gg(i, p[i]);
p[i] = p[i - 1] * p[i];
gg(n - i + 1, q[i]);
q[i] = q[i - 1] * q[i];
}
}
ll C(int x) {
nod w = q[x] / p[x];
ll s = w.v[0];
fo(i, 1, u0) s = s * ksm(u[i], w.v[i]) % mo;
return s;
} ll f[105][10005]; int main() {
freopen("islands.in", "r", stdin);
freopen("islands.out", "w", stdout);
scanf("%d", &T);
fo(ii, 1, T) {
scanf("%d %d %d %d", &n, &m, &k, &mo);
if(n - m == 0) {
pp("%d\n", 1 % mo);
continue;
}
fen(mo);
fo(i, 0, 10000) {
c[i][0] = 1;
fo(j, 1, min(i, 100)) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mo;
}
build(n - m);
memset(f, 0, sizeof f);
fo(i, 1, m) {
f[i][0] = 1;
fo(j, 1, i * k) {
f[i][j] = f[i][j - 1] * i;
if(j >= k + 1) f[i][j] -= f[i - 1][j - (k + 1)] * c[j - 1][k] % mo * i;
f[i][j] = (f[i][j] % mo + mo) % mo;
}
}
ll ans = 0;
fo(i, 0, m * k) {
if(n - m - 1 - i >= 0) ans += f[m][i] * C(i) % mo * i % mo * ksm(n - m, n - m - 1 - i) % mo;
if(n - m - 1 - i == -1) ans += f[m][i] * C(i) % mo;
}
build(n);
ans = ans % mo * C(m) % mo;
pp("%lld\n", ans);
}
}

【NOI2019模拟2019.7.1】为了部落 (生成森林计数,动态规划)的更多相关文章

  1. [JZOJ6247]【NOI2019模拟2019.6.27】C【计数】

    Description n<=200000 Solution 比赛时没做出这道题真的太弟弟了 首先我们从小到大插入数i,考虑B中有多少个区间的最大值为i 恰好出现的次数不太好计算,我们考虑计算最 ...

  2. [JZOJ6244]【NOI2019模拟2019.7.1】islands【计数】【图论】

    Description n<=1e9,M,K<=100 Solution 显然任选m个港口的答案是一样的,乘个组合数即可. 考虑枚举m个港口的度数之和D 可以DP计算 记\(F_{m,D} ...

  3. [JZOJ6244]【NOI2019模拟2019.7.1】Trominoes 【计数】

    Description n,m<=10000 Solution 考虑暴力轮廓线DP,按顺序放骨牌 显然轮廓线长度为N+M 轮廓线也是单调的 1表示向上,0表示向右 N个1,M个0 只能放四种骨牌 ...

  4. [JZOJ6241]【NOI2019模拟2019.6.29】字符串【数据结构】【字符串】

    Description 给出一个长为n的字符串\(S\)和一个长为n的序列\(a\) 定义一个函数\(f(l,r)\)表示子串\(S[l..r]\)的任意两个后缀的最长公共前缀的最大值. 现在有q组询 ...

  5. 【NOI2019模拟2019.6.29】字符串(SA|SAM+主席树)

    Description: 1<=n<=5e4 题解: 考虑\(f\)这个东西应该是怎样算的? 不妨建出SA,然后按height从大到小启发式合并,显然只有相邻的才可能成为最优答案.这样的只 ...

  6. 【NOI2019模拟2019.6.29】组合数(Lucas定理、数位dp)

    Description: p<=10且p是质数,n<=7,l,r<=1e18 题解: Lucas定理: \(C_{n}^m=C_{n~mod~p}^{m~mod~p}*C_{n/p} ...

  7. 【NOI2019模拟2019.7.4】朝夕相处 (动态规划+BM)

    Description: 题解: 这种东西肯定是burnside引理: \(\sum置换后不动点数 \over |置换数|\) 一般来说,是枚举置换\(i\),则\(对所有x,满足a[x+i]=a[i ...

  8. 【NOI2019模拟2019.6.27】B (生成函数+整数划分dp|多项式exp)

    Description: \(1<=n,k<=1e5,mod~1e9+7\) 题解: 考虑最经典的排列dp,每次插入第\(i\)大的数,那么可以增加的逆序对个数是\(0-i-1\). 不难 ...

  9. 【NOI2019模拟2019.7.1】三格骨牌(轮廓线dp转杨图上钩子定理)

    Description \(n,m<=1e4,mod ~1e9+7\) 题解: 显然右边那个图形只有旋转90°和270°后才能放置. 先考虑一个暴力的轮廓线dp: 假设已经放了编号前i的骨牌,那 ...

随机推荐

  1. HTML事件处理程序---内联onclick事件

    HTML事件处理程序绑定方法: <input type="button" value="click me" onclick="show(this ...

  2. KindEditor在eclipse里的配置方法

    KindEditor介绍: kindEditor是一款国产富文本编辑器,类似fckeditor和目前比较流行的百度Ueditor.其产品官方网站为http://kindeditor.net/ Kind ...

  3. spark算子之Aggregate

    Aggregate函数 一.源码定义 /** * Aggregate the elements of each partition, and then the results for all the ...

  4. ApiCloud如何一键真机测试

    首先假设你已经有了APICloud账号,并创建了App项目. 需要注意的是,手机和电脑需要连接在同一wifi环境下. 第一步 下载自定义Loader 进入“开发控制台” 点击我们的项目 选择模块,再选 ...

  5. springCloud数据

    DROP DATABASE IF EXISTS springcloud_db01;CREATE DATABASE springcloud_db01 CHARACTER SET utf8;USE spr ...

  6. Kubernetes重大漏洞?阿里云已第一时间全面修复

    近日,Kubernetes社区发现安全漏洞 CVE-2018-1002105,阿里云容器服务已在第一时间完成全面修复,敬请广大用户登录阿里云控制台升级Kubernetes版本. 目前Kubernete ...

  7. Vuex 常规用法

    背景 很多时候我们已经熟悉了框架的运用,但是有时候就是忘了怎么用 所以这里想记下大部分的框架使用方法,方便使用的时候拷贝 一.安装 npm 方式 npm install vuex --save yar ...

  8. 建模+线性dp——cf1201D

    这类题目要首先把模型建立起来,挑选一个好的状态能让dp方程简化很多 /* dp[i][0]表示从右到左,最后停在左端 dp[i][1]表示从左到右,最后停在右端 dp[i+1][0]=min(dis( ...

  9. TESTNG听录音笔记

    1. 是什么:有了它可以管理测试用例,做数据驱动,多线程模式下case的鲍旭类型 2. 如何生成testng的xml文件 -- based on Eclipse Eclipse里装上testn插件,指 ...

  10. python 内置模块--collections

    1.计数器(counter) Counter是对字典的补充,用于追踪值出现的次数. Counter具有字典的全部属性和自己的属性. >>>import collections obj ...