P4797 [CEOI 2015] 波将金的路径
题目要我们找一个环长 \(\ge 4\) 的环,使得这个环没有弦。难点显然在这个没有弦的限制。如果我们直接找最小环,可能会找到一个三元环,虽然没有弦了,但也不满足题目的要求。
一个人类智慧的点边转换,考虑对边之间连边。初步想法是将 \((u, v)\) 与 \((v, w)\) 连边,条件是不存在边 \((u,w)\),这样就手动把三元环排除掉了。可以发现,直接在新图上找最小环(肯定没有弦)就行了。
但这样还是有问题,如果按照无向边来处理的话,如果原图是菊花图,建出来的新图依然有环。那么我们把无向边拆成有向边,还是按照上面的方法建新图,这样新图上的环和原图上的非三元环存在一一对应关系。
最小环跑 \(floyd\)?没必要。仔细思考我们只是要找到一个极小环,直接 dfs,然后处理返祖边就行。具体来说,从上到下 dfs,找到 \(u\) 的返祖边中深度最大的那个(如果存在),直接返回就行,这样找到的一定是极小环。因为如果还有更小的环的话在 \(u\) 的祖先处就被遍历到了。
复杂度在于建图的 \(O(N^3)\)。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5, M = 2e5 + 5;
int n, m, g[N][N], ver[M][2], dep[M], st[M], tp;
vector<int> e[M];
bitset<M> in;
void dfs(int u, int fa){dep[u] = dep[fa] + 1;in[st[++tp] = u] = 1;int x = 0;for(int v : e[u]){if(in[v] && dep[x] < dep[v]) x = v;}if(x){cout << ver[u][1] << ' ';--tp;while(1){cout << ver[st[tp]][1] << ' ';if(st[tp] == x) exit(0);--tp;}}for(int v : e[u]){if(!dep[v]) dfs(v, u);}in[st[tp--]] = 0;
}
int main(){cin.tie(nullptr)->sync_with_stdio(0);cin >> n >> m;for(int i = 1; i <= m; ++i){int &u = ver[i][0], &v = ver[i][1];cin >> u >> v;g[u][v] = i;g[v][u] = m + i;ver[m + i][0] = v, ver[m + i][1] = u;}for(int v = 1; v <= n; ++v){for(int u = 1; u <= n; ++u){for(int w = u + 1; w <= n; ++w){if(g[u][v] && g[v][w] && !g[u][w]){e[g[u][v]].emplace_back(g[v][w]);e[g[w][v]].emplace_back(g[v][u]);}}}}for(int i = 1; i <= 2 * m; ++i){if(!dep[i]) dfs(i, 0);}cout << "no";return 0;
}
P6620 [省选联考 2020 A 卷] 组合数问题
普通幂转下降幂的 trick。
为什么要引入下降幂呢,主要是由于求导的性质,如果 \(f(k)\) 是关于 \(k\) 的函数。
并且 \(F(x) = \sum_k f(k)x ^k\)。
两边求 \(i\) 阶导,\(F^{(i)}(x) = \sum_k f(k)k^{\underline{i}} x ^ {k - i}\)。
也就是说,如果我们知道 \(F(x)\) 的封闭形式,那么推广到 \(x\) 的系数是 \(k\) 的下降幂多项式的形式的幂级数也是有封闭形式的。
考虑如何把普通幂多项式转化成下降幂多项式。首先要知道一个式子 \(m^n = \sum_{i = 0}^m {n \brace i}m^{\underline{i}}\)。证明的话考虑组合意义,都是把 \(n\) 个有标号的球放进 \(m\) 的有标号的盒子里。
那么对于 \(\sum_{i = 0}^m a_ix^i\) 的 \(m\) 次多项式,套用刚刚的方法,就变成了 \(\sum_{i = 0}^m a_i(\sum_{k = 0}^i {i \brace k}x^{\underline{k}})\),整理一下,也就是 \(\sum_{i = 0}^m (\sum_{j = i}^m a_j{j \brace i})x^{\underline{i}}\)。
对于原式子 \(\sum_{k = 0}^n G(k){n \choose k}x^k\)。其中 \(G(k)\) 是关于 \(k\) 的 \(m\) 次多项式。由于 \(F(x) = \sum_{k = 0}^n{n \choose k}x^k\) 有封闭形式 \(F(x) = (1 + x)^n\)。考虑对其两边求 \(i\) 阶导,也就是说 \(\sum_{k = 0}^n {n \choose k} k^{\underline{i}} x^{k - i} = n^{\underline{i}}(1 + x)^{n - i}\)。两边同时乘 \(x^i\),\(\sum_{k = 0}^n {n \choose k} k^{\underline{i}} x^{k} = n^{\underline{i}}(1 + x)^{n - i}x^i\)。用刚刚的方法把 \(G(k)\) 转成 \(\sum_{t = 0}^m b_t\times k^{\underline{t}}\) 的形式,整理求和号 \(\sum_{k = 0}^n (\sum_{i = 0}^m b_i\times k^{\underline{i}}) {n \choose k}x^k = \sum_{i = 0}^m b_i (\sum_{k = 0}^n {n \choose k}k^{\underline{i}} x^k) = \sum_{i = 0}^m b_i n^{\underline{i}}(1 + x)^{n - i}x^i\) 就做完了。
递推斯特林数时,注意一下初值怎么赋。\({i \brace 0} = [i = 0]\)。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int M = 1e3 + 5;
int mod, x, n, m;
ll b[M], a[M], f[M][M];
ll qpow(ll a, ll b){ll ret = 1;for(int x = b; x; x >>= 1, (a *= a) %= mod){if(x & 1) (ret *= a) %= mod;}return ret;
}
int main(){cin.tie(nullptr)->sync_with_stdio(0);cin >> n >> x >> mod >> m;for(int i = 0; i <= m; ++i) cin >> a[i];f[0][0] = 1;for(int i = 0; i <= m; ++i){for(int j = 1; j <= i; ++j){f[i][j] = (f[i - 1][j - 1] + j * f[i - 1][j]) % mod;}}for(int i = 0; i <= m; ++i){for(int j = i; j <= m; ++j)(b[i] += a[j] * f[j][i]) %= mod;}ll ans = 0, tmp = 1;for(int i = 0; i <= m; ++i){(ans += b[i] * tmp % mod * qpow(x, i) % mod * qpow(x + 1, n - i) % mod) %= mod;(tmp *= (n - i)) %= mod;}cout << ans;return 0;
}