城市规划
题目大意
给你 n,要你求 n 个点简单有标号无向连通图的个数。
思路
设 gi 为 n 个点的简单有标号图个数, fi 则是满足条件的。
gi=2(i2)
fn=gn−n−1∑i=1(n−1i−1)fign−i
gn=n∑i=1(n−1i−1)fign−i
gn=n∑i=1(n−1)!(i−1)!(n−i)fign−i
gn(n−1)!=n∑i=1fi(i−1)!gn−i(n−i)!
然后不难看出可以看成三个多项式,用个多项式求逆加多项式乘法即可。
代码
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define mo 1004535809
#define cpy(f, g, x) memcpy(f, g, sizeof(int) * (x))
#define clr(f, x) memset(f, 0, sizeof(int) * (x))
using namespace std;
const int N = 130000 * 8 + 5;
int n, g[N], g1[N], g2[N];
int jc[N], inv[N], invs[N];
int add(int x, int y) {return x + y >= mo ? x + y - mo : x + y;}
int dec(int x, int y) {return x < y ? x - y + mo : x - y;}
int mul(int x, int y) {return 1ll * x * y % mo;}
int ksm(int x, int y) {int re = 1; while (y) {if (y & 1) re = mul(re, x); x = mul(x, x); y >>= 1;} return re;}
struct Poly {
int G, Gv, an[N];
vector <int> Wn[21], Wn_[21];
void Init() {
G = 3; Gv = ksm(G, mo - 2);
for (int mid = 1, d = 0; mid < N; mid <<= 1, d++) {
int w = ksm(G, (mo - 1) / (mid << 1)), wv = ksm(Gv, (mo - 1) / (mid << 1));
for (int j = 0, wn = 1, wn_ = 1; j < mid; j++, wn = mul(wn, w), wn_ = mul(wn_, wv)) {
Wn[d].push_back(wn); Wn_[d].push_back(wn_);
}
}
}
void get_an(int limit, int l_size) {
for (int i = 0; i < limit; i++) an[i] = (an[i >> 1] >> 1) | ((i & 1) << (l_size - 1));
}
void NTT(int *f, int limit, int op) {
for (int i = 0; i < limit; i++) if (i < an[i]) swap(f[i], f[an[i]]);
for (int mid = 1, d = 0; mid < limit; mid <<= 1, d++)
for (int R = mid << 1, j = 0; j < limit; j += R)
for (int k = 0; k < mid; k++) {
int x = f[j | k], y = mul(f[j | mid | k], (op == 1) ? Wn[d][k] : Wn_[d][k]);
f[j | k] = add(x, y); f[j | mid | k] = dec(x, y);
}
if (op == -1) {
int limv = ksm(limit, mo - 2);
for (int i = 0; i < limit; i++) f[i] = mul(f[i], limv);
}
}
void px(int *f, int *g, int limit) {
for (int i = 0; i < limit; i++) f[i] = mul(f[i], g[i]);
}
void times(int *f, int *g, int n, int m, int T) {
static int tmp[N];
int limit = 1, l_size = 0; while (limit < n + m) limit <<= 1, l_size++;
get_an(limit, l_size);
clr(f + n, limit - n); cpy(tmp, g, m); clr(tmp + m, limit - m);
NTT(f, limit, 1); NTT(tmp, limit, 1); px(f, tmp, limit); NTT(f, limit, -1);
clr(f + T, limit - T); clr(tmp, limit);
}
void invp(int *f, int n) {
static int w[N], r[N], tmp[N];
w[0] = ksm(f[0], mo - 2);
int limit = 1, l_size = 0;
for (int len = 2; (len >> 1) <= n; len <<= 1) {
limit = len; l_size++; get_an(limit, l_size);
cpy(r, w, len >> 1);
cpy(tmp, f, limit); NTT(tmp, limit, 1);
NTT(r, limit, 1); px(r, tmp, limit);
NTT(r, limit, -1); clr(r, limit >> 1);
cpy(tmp, w, len); NTT(tmp, limit, 1);
NTT(r, limit, 1); px(r, tmp, limit);
NTT(r, limit, -1);
for (int i = (len >> 1); i < len; i++)
w[i] = dec(mul(w[i], 2), r[i]);
}
cpy(f, w, n); clr(w, limit); clr(r, limit); clr(tmp, limit);
}
}P;
void Init() {
P.Init();
jc[0] = 1; for (int i = 1; i < N; i++) jc[i] = mul(jc[i - 1], i);
inv[0] = inv[1] = 1; for (int i = 2; i < N; i++) inv[i] = mul(inv[mo % i], mo - mo / i);
invs[0] = 1; for (int i = 1; i < N; i++) invs[i] = mul(invs[i - 1], inv[i]);
}
int main() {
Init();
scanf("%d", &n);
for (int i = 0; i <= n; i++) g[i] = ksm(2, 1ll * i * (i - 1) / 2 % (mo - 1));
for (int i = 1; i <= n; i++) g1[i] = mul(g[i], invs[i - 1]);
for (int i = 0; i <= n; i++) g2[i] = mul(g[i], invs[i]);
P.invp(g2, n + 1); P.times(g1, g2, n + 1, n + 1, n + 1);
printf("%d", mul(g1[n], jc[n - 1]));
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现