【NOI2019模拟2019.7.1】为了部落 (生成森林计数,动态规划)
Description:

\(1<=n<=1e9,1<=m,k<=100\)
模数不是质数。
题解:
先选m个点,最后答案乘上\(C_{n}^m\)。
不妨枚举m个点的度数和D,那么我们需要解决两个问题:
- 一共m个有标号盒子,D个有标号小球放到盒子里,且每个盒子的球数不超过k的方案数。
- 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】为了部落 (生成森林计数,动态规划)的更多相关文章
- [JZOJ6247]【NOI2019模拟2019.6.27】C【计数】
Description n<=200000 Solution 比赛时没做出这道题真的太弟弟了 首先我们从小到大插入数i,考虑B中有多少个区间的最大值为i 恰好出现的次数不太好计算,我们考虑计算最 ...
- [JZOJ6244]【NOI2019模拟2019.7.1】islands【计数】【图论】
Description n<=1e9,M,K<=100 Solution 显然任选m个港口的答案是一样的,乘个组合数即可. 考虑枚举m个港口的度数之和D 可以DP计算 记\(F_{m,D} ...
- [JZOJ6244]【NOI2019模拟2019.7.1】Trominoes 【计数】
Description n,m<=10000 Solution 考虑暴力轮廓线DP,按顺序放骨牌 显然轮廓线长度为N+M 轮廓线也是单调的 1表示向上,0表示向右 N个1,M个0 只能放四种骨牌 ...
- [JZOJ6241]【NOI2019模拟2019.6.29】字符串【数据结构】【字符串】
Description 给出一个长为n的字符串\(S\)和一个长为n的序列\(a\) 定义一个函数\(f(l,r)\)表示子串\(S[l..r]\)的任意两个后缀的最长公共前缀的最大值. 现在有q组询 ...
- 【NOI2019模拟2019.6.29】字符串(SA|SAM+主席树)
Description: 1<=n<=5e4 题解: 考虑\(f\)这个东西应该是怎样算的? 不妨建出SA,然后按height从大到小启发式合并,显然只有相邻的才可能成为最优答案.这样的只 ...
- 【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} ...
- 【NOI2019模拟2019.7.4】朝夕相处 (动态规划+BM)
Description: 题解: 这种东西肯定是burnside引理: \(\sum置换后不动点数 \over |置换数|\) 一般来说,是枚举置换\(i\),则\(对所有x,满足a[x+i]=a[i ...
- 【NOI2019模拟2019.6.27】B (生成函数+整数划分dp|多项式exp)
Description: \(1<=n,k<=1e5,mod~1e9+7\) 题解: 考虑最经典的排列dp,每次插入第\(i\)大的数,那么可以增加的逆序对个数是\(0-i-1\). 不难 ...
- 【NOI2019模拟2019.7.1】三格骨牌(轮廓线dp转杨图上钩子定理)
Description \(n,m<=1e4,mod ~1e9+7\) 题解: 显然右边那个图形只有旋转90°和270°后才能放置. 先考虑一个暴力的轮廓线dp: 假设已经放了编号前i的骨牌,那 ...
随机推荐
- ubuntu颜色配置
对于刚接触ubuntu的同学们,打开终端(ctrl+alt+T),会发现里面都是一个颜色,不管是用户名.主机名还是命令都是白色,当然,用 ls 列出文件的时候是会多一种颜色的.即使这样,对开发人员来说 ...
- java笔试题大全带答案(经典11题)
1.不通过构造函数也能创建对象吗()A. 是B. 否分析:答案:AJava创建对象的几种方式(重要):(1) 用new语句创建对象,这是最常见的创建对象的方法.(2) 运用反射手段,调用java.la ...
- node-mysql连接数据库
const mysql=require('mysql') const con=mysql.createConnection({ host:'localhost', user:'root', passw ...
- (一)CGI (通用网关接口) 简介
CGI (通用网关接口)公共网关接口(Common Gateway Interface,CGI)是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能.CGI 应用程序能与浏览器 ...
- 【TCP】SYN攻击
TCP握手协议 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接.第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确 ...
- JavaWeb开发中遇到的错误:org.apache.catalina.core.StandardWrapperValve invoke
org.apache.catalina.core.StandardWrapperValve invoke 今天写代码,竟然接连遇到这个异常好几次.debug几个小时才弄明白,晕. 上网找了些拼凑下做个 ...
- 芜湖市2018市队选拔Day2T1
好激动啊,Day2竟然AK了! Day2T1养宠物 Descriptionbadgers是可爱的动物,Smart想拥有一些.宠物店提供N个badgers,编号为1..N,Smart都很喜欢,所以他想拥 ...
- delphi 数据处理
TStringStream 怎样转换String procedure TForm1.Button1Click(Sender: TObject); var ss:TStringStream; str:S ...
- AcWing 160. 匹配统计 (哈希+二分) 打卡
阿轩在纸上写了两个字符串,分别记为A和B. 利用在数据结构与算法课上学到的知识,他很容易地求出了“字符串A从任意位置开始的后缀子串”与“字符串B”匹配的长度. 不过阿轩是一个勤学好问的同学,他向你提出 ...
- js中的函数声明和函数表达式的区别
目录 一.声明与表达式的格式 1.1 声明式的格式: 1.2 表达式的格式: 二.区别 2.1 函数表达式可以直接在后面加括号执行,而函数声明不可以. 2.2 函数表达式可以被提前解析出来 2.3 命 ...