Gravatar
rzzakioi
积分:631
提交:99 / 207

# T5 数据结构题 题解

我们将询问转化为如下问题

定义 $f(l,r)$ $(l \le r)$ 如下:

当 $l=r$ 时,$f(l,r)=a_l$;否则,$f(l,r)=a_l^{f(l+1,r)}$

每次操作 $2$,输出 $f(l,r)\bmod p$

那我们该如何维护呢?

这里我们需要一个前置知识,叫做扩展欧拉定理,即:

$$a^b\equiv \begin{cases}a^{b \bmod \varphi(p)}(b<\varphi(p))\\a^{b \bmod \varphi(p)+\varphi(p)}(b\ge\varphi(p))\end{cases}\pmod p$$

那么

$$f(l,r)\equiv a^{f(l+1,r)\bmod \varphi(p)+[b\ge \varphi(p)]\times \varphi(p)}\pmod p$$

那么,求解 $f(l,r)\bmod p$ 就变成了求解 $f(l+1,r)\bmod \varphi(p)$,并判断 $f(l+1,r)$ 与 $p$ 的关系

然后我们就可以递归求解了

递归边界的话,则有三个

当 $l=r$ 时,$f(l,r)\bmod p\equiv a_l\pmod p$

当 $p=1$ 时,$f(l,r)\bmod p\equiv 0\pmod p$

当 $a_l=1$ 时,$f(l,r)\bmod p\equiv 1\pmod p$

那么这个递归的复杂度是多少呢?

我们分析一下将 $\varphi(p)\to p$ 直到 $p=1$ 的时间复杂度是多少

由 $\varphi(p)=p\prod_{i=1}^{n} \frac{p_i-1}{p_i}$ $(p=\prod_{i=1}^{n}{p_i}^{k_i})$可知

当 $p$ 为偶数时,上述式子必有一项是 $\frac{1}{2}$,所以 $\varphi(p)\le\frac{1}{2}p$

当 $p$ 为奇数时,上述式子的 $p_i-1$ 必是偶数,因此 $\varphi(p)$ 为偶数,回到 $p$ 为偶数的情况

因此最多经过 $O(logn)$ 次递归就可以得出答案

至于判断 $f(l+1,r)$ 与 $p$ 的关系,可以让递归函数返回一个结构体,结构体第一项存 $f(l+1,r)\bmod \varphi(p)$ 的值,第二项存 $f(l+1,r)$ 与 $\varphi(p)$ 的大小关系,前者大于等于后者,这一项就是 $1$,反之则为 $0$

区间和我们用树状数组维护差分即可

时间复杂度:$O(mlogqlogn)$


题目4318  数据结构题 AAAAAAAAAA      3      评论
2026-02-28 13:04:35    
Gravatar
终焉折枝
积分:1952
提交:243 / 421
真的更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19648133

题目4323  十二重计数法(第一关) AAAAA      5      评论
2026-02-27 20:33:10    
Gravatar
终焉折枝
积分:1952
提交:243 / 421
真的更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19648133

题目4324  十二重计数法(第二关) AAAAA      5      评论
2026-02-27 20:32:11    
Gravatar
HXF
积分:7236
提交:1328 / 2789

f[i][j]统一定义为i个小球放到j个盒子里的方案数

1.m^n

2.m!/(m-n)!

3.f[i][j]=j*f[i-1][j]+(m-j+1)*f[i-1][j-1]

  考虑容斥可以O(n)

4.第二类斯特林数S[n][1]+...+S[n][m]

  O(n^2)直接递推S[i][j]=S[i-1][j-1]+j*S[i-1][j]

  O(nlogn)NTT,斯特林数反演

5.[n<=m]

6.S[n][m]

7.C(n+m-1,m-1)

8.C(m,n)

9.C(n-1,m-1)

10.f[i][j]=f[i][j-1]+f[i-j][j]

    考虑球的数量>=根号n的盒子的个数<=根号n个可以分块O(n^1.5)

    五边形数定理可优化到O(nlogn)

11.[n<=m]

12.f[i][j]=f[i-1][j-1]+f[i-j][j]  自然数拆分

    同10



题目4323  十二重计数法(第一关)      1      评论
2026-02-27 15:23:31    
Gravatar
终焉折枝
积分:1952
提交:243 / 421

更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19645638

更好的阅读体验(加强版):https://www.cnblogs.com/To-Carpe-Diem/p/19645952

$\newcommand{\binom}[2]{{#1 \choose #2}}$

大意

$n$ 对情侣,恰好 $k$ 对在 $2 \times n$ 的电影院中坐一起的方案数。


思路

很好的题目,使得我的大脑旋转。

首先我们先定义函数 $f(n, k)$ 表示 $n$ 对情侣有 $k$ 对恰好坐到一块的方案数。首先我们要从 $n$ 对情侣中选择 $k$ 对,这个是 $\binom{n}{k}$ 的,再从 $n$ 排选择 $k$ 排,这个也是 $\binom{n}{k}$ 的,考虑到这 $k$ 对情侣间也有先后顺序,贡献就是 $k!$,两人可以换位,贡献是 $2 ^ k$,剩下有 $n - k$ 对情侣,我们只需要将其错排即可。

定义 $g(m)$ 表示 $m$ 对情侣错排的方案数,我们考虑这个怎么求。这个我们可以利用容斥原理,我们考虑直接计算至少 $j$ 对和睦的方案数,那么这个的贡献与上面的类似,应当是 $\binom{m}{j} ^ 2 j! 2 ^ j (2(m - j))!$,那么由容斥可知:$g(m) = \sum_{j = 0} ^ {m}(-1) ^ j \binom{m}{j} ^ 2 j! 2 ^ j (2(m - j))!$

那么我们合并一下整个式子,就是:$f(n, k) = \binom{n}{k} ^ 2 k ! 2 ^ k g(n - k) = \binom{n}{k} ^ 2 k ! 2 ^ k \sum_{j = 0} ^ {n - k}(-1) ^ j \binom{n - k}{j} ^ 2 j! 2 ^ j (2(n - k - j))!$

这个式子我们是可以直接求的,于是就完成了。

接下来进入正题,我们注意到刚刚的弱化版内的 $f(n, k) = \binom{n}{k} ^ 2 k ! 2 ^ k D(n - k) $,且 $D(n) = \sum_{i = 0} ^ {n} (-1) ^ i\binom{n}{i} ^ 2 i! 2 ^ i (2(n - i))!$,我们考虑对于 $D(n)$ 进行化简.

$\sum_{i = 0} ^ {n}(-1) \binom{n}{i} ^ 2 i! 2 ^ i (2(n - i)) != (n!) ^ 2 \sum_{i = 0} ^ n \frac{(2(n - i))!}{(n - i)! ^ 2} \frac{(-2) ^ i}{i!}$

这里我们就可以直接做卷积了,但是问题在于这个题目要求的复杂度太过苛刻,卷积无法通过此题,我们考虑构造生成函数。

我们设:$F(x) = (n!) ^ 2 \sum_{i = 0} ^ n \frac{(2(n - i))!}{(n - i)! ^ 2} \frac{(-2) ^ i}{i!} x^i,G[n] = \frac{(2n)!}{n!^2},P[n] = \frac{(-2) ^ i}{n !}$。那么,由离散卷积可知:$F(x) = G(x) * P(x)$

接下来,我们只需要分别对于 $G(x)$ 和 $P(x)$ 化简即可。首先,对于 $G(x)$ 来说,我们可以利用**广义二项式定理**,那么什么是广义二项式定理呢?对于任意实数 $\alpha$,都有 $(1 + z) ^ \alpha = \sum_{i = 0} ^ {\infty} \binom{\alpha}{i} z ^ i$,那么说我们就可以化简 $G(x)$ 了:$G(x) = \sum_{i = 0} \frac{(-2x) ^ i}{i!} x ^ i = \sum_{i = 0} \binom{2i}{i}x^i = \frac{1}{\sqrt{1 - 4x}}$对于 $P(x)$ 来说,直接化简:$P(x) = \sum_{i = 0} \frac{(-2x)!}{i!} = e ^ {-2x}$

经过化简,我们可以最终得到:$F(x) = \frac{e ^ {-2x}}{\sqrt{1 - 4x}}$对于这个生成函数,为了得到与自身相关的式子进行递推,我们选择对其求导。$F'(x) = \frac{8x * e ^ {-2x}}{(1 - 4x) ^ \frac{3}{2}} = \frac{8x}{1 - 4x} F(x)$整理一下:$F'(x) = 4xF'(x) + 8xF(x)$。提取 $[x ^ n]$ 的系数:$F'[n] = 4xF'[n - 1] + 8xF[n - 1]$。稍施小计,我们可以直接得到关于 $F[n]$ 的递推式:$(n + 1)F[n + 1] = 4nF[n] + 8F[n - 1]$

你可能以为万事大吉了,但是问题是我们还有个 $(n!) ^ 2$ 的系数在最开始被我们提出去了,我们要把这玩意弄回来,带入 $F[n] = \frac{D(n)}{n!} ^ 2$:$(n + 1)\frac{D[n + 1]}{(n + 1)!} = 4n \frac{D[n]}{n!^2} + 8 \frac{D[n - 1]}{(n - 1)! ^ 2}$我们就可以得到关于 $D[n]$ 的递推式:$D[n + 1] = 4n(n + 1)D[n] + 8n ^ 2(n + 1)D[n - 1]$

直接 $O(n)$ 递推即可。


代码

#include<iostream>
using namespace std;

#define int long long
const int MOD = 998244353;
const int MAXN = 2e3 + 5;

int C[MAXN][MAXN];
int fac[MAXN];
int g[MAXN], pow2[MAXN];

void init(){
	C[0][0] = 1;
	for(int i = 1;i < MAXN;i ++){
		C[i][0] = C[i][i] = 1;
		for(int j = 1;j < i;j ++){
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
		}
	}
	fac[0] = 1;
	for(int i = 1;i < MAXN;i ++){
		fac[i] = (fac[i - 1] * i) % MOD;
	}
	pow2[0] = 1;
	for(int i = 1;i < MAXN;i ++){
		pow2[i] = pow2[i - 1] * 2 % MOD;
	}
	for(int m = 0;m <= 1000;m ++){
		g[m] = 0;
		for(int j = 0;j <= m;j ++){
			int a = (j % 2 == 0) ? 1 : MOD - 1;
			int res = C[m][j] * C[m][j] % MOD;
			res = (res * fac[j]) % MOD;
			res = (res * pow2[j]) % MOD;
			res = (res * fac[2 * (m - j)]) % MOD;
			res = (res * a) % MOD;
			g[m] = (g[m] + res) % MOD;
		}
 	}
	return;
}

int f(int n, int k){
	int res = (C[n][k] * C[n][k]) % MOD;
	res = (res * fac[k]) % MOD;
	res = (res * pow2[k]) % MOD;
	res = (res * g[n - k]) % MOD;
	return res;
}

int T, n, k;

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	init();

	cin >> T;

	while(T --){
		cin >> n;
		for(int i = 0;i <= n;i ++){
			cout << f(n, i) << '\n';
		}
	}

	return 0;
}


题目3353  情侣?给我烧了 A      5      评论
2026-02-27 11:52:19    
Gravatar
终焉折枝
积分:1952
提交:243 / 421

更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19617614


大意

每一个小朋友在得到自己喜欢的饮料和食物的时候,就实现了其愿望,问最多能实现多少奶牛的愿望。


思路

首先我们考虑这样的三元关系 $(饮料,小朋友,食物)$,这样的三元关系,如果我们直接在中间连边去跑最大流是否有问题呢?

显然是有问题的,如下图:

这样我们发现个奇怪的问题,一个小朋友对应的喜欢的食物和饮料其实是很多的,但是这样会使得不同的饮料与食物都对一个小朋友产生影响,现在我们需要做的是给一个点上约束,而这种技巧叫做拆点。

我们考虑将每个小朋友拆为 $in$ 和 $out$,在 $in$ 和 $out$ 间连容量为 $1$ 的边,这样我们就可以保证每个小朋友只被算一次,如下图:

在拆完点之后直接跑最大流即可。


代码

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAX_N = 1000;
const int MAX_M = 100000;
struct edge {
    int v, c, next;
} e[MAX_M];
int p[MAX_N], eid;
void init() {
    memset(p, -1, sizeof(p));
    eid = 0;
}
void insert(int u, int v, int c) {
    e[eid].v = v;
    e[eid].next = p[u];
    e[eid].c = c;
    p[u] = eid++;
}
void addedge(int u, int v, int c) {
    insert(u, v, c);
    insert(v, u, 0);
}
int S, T;
int d[MAX_N];
bool bfs() {
    memset(d, -1, sizeof(d));
    queue<int> q;
    q.push(S);
    d[S] = 0;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = p[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if (e[i].c > 0 && d[v] == -1) {
                q.push(v);
                d[v] = d[u] + 1;
            }
        }
    }
    return (d[T] != -1);
}
int dfs(int u, int flow) {
    if (u == T) {
        return flow;
    }
    int res = 0;
    for (int i = p[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if (e[i].c > 0 && d[u] + 1 == d[v]) {
            int tmp = dfs(v, min(flow, e[i].c));
            flow -= tmp;
            e[i].c -= tmp;
            res += tmp;
            e[i ^ 1].c += tmp;
            if (flow == 0) {
                break;
            }
        }
    }
    if (res == 0) {
        d[u] = -1;
    }
    return res;
}
int Dinic() {
    int res = 0;
    while (bfs()) {
        res += dfs(S, INF);
    }
    return res;
}
int main() {
    init();
    int n, m, k;
    cin >> n >> m >> k;
    for(int i = 1;i <= n;i ++){
        addedge(m + i, m + n + i, 1);
        int a, b; cin >> a >> b;
        for(int j = 0;j < a;j ++){
            int x; cin >> x;
            addedge(x, m + i, 1);
        }
        for(int j = 0;j < b;j ++){
            int x; cin >> x;
            addedge(m + n + i, m + 2 * n + x, 1);
        }
    }
    S = 0; T = m + 2 * n + k + 1;
    for(int i = 1;i <= m;i ++){
        addedge(S, i, 1);
    }
    for(int i = 1;i <= k;i ++){
        addedge(m + 2 * n + i, T, 1);
    }
    cout << Dinic() << endl;
    return 0;
}


题目2727  [USACO Open07]牛的进餐      7      2 条 评论
2026-02-26 12:01:19