QOJ15801 Tournament
原题链接:Tournament
分析
原来要科普竞赛图吗?
竞赛图就是无向完全图给每条边加上一个任意的方向。
我目前只知道 竞赛图SCC缩点之后会变成一条链 ,但是还不清楚具体怎么计数......
难道今天我要先学 Tarjan 吗?
我觉得可以进行一下 DP,别管为什么,反正你肯定要写这一步!
我们设 d p i dp_{i} dpi 表示缩点之后的链长为 i i i 的竞赛图的个数吗?
但是,这个真的可以转移吗?我觉得前途很有限......
呃......难道说,我们发现,这个东西可以递归下去?那也不对啊......
什么?我竟然开错题了😭
我们设 f n f_n fn 表示 n n n 个点的强连通竞赛图数量, g n g_n gn 表示 n n n 个点的竞赛图数量。
可知:
f n = g n − ∑ i = 1 n − 1 C n i × f i × g n − i f_n=g_n-\sum\limits_{i=1}^{n-1}{C_{n}^{i}\times f_i\times g_{n-i}} fn=gn−i=1∑n−1Cni×fi×gn−i
那么,我们设 d p i , j dp_{i,j} dpi,j 表示缩点之后链长为 j j j,有 i i i 个点的图的个数。
那么,有转移:
d p i , j = ∑ k = 1 i C n k × f k × d p i − k , j − 1 dp_{i,j}=\sum\limits_{k=1}^{i}{C_{n}^{k}\times f_{k}\times dp_{i-k,j-1}} dpi,j=k=1∑iCnk×fk×dpi−k,j−1
这个转移是 O ( n 3 ) O(n^3) O(n3) 的。答案是 ∑ j = 1 n d p n , j \sum\limits_{j=1}^{n}dp_{n,j} j=1∑ndpn,j。
我们使用 EGF,设 F ( x ) = ∑ f n × x n n ! F(x)=\sum{f_n\times\frac{x^n}{n!}} F(x)=∑fn×n!xn,答案即为 n ! × x n × F ( x ) ( 1 − F ( x ) ) 2 n!\times x\^n\times\frac{F(x)}{(1-F(x))^2} n!×xn×(1−F(x))2F(x),其中, k k k 为枚举的链长。使用多项式快速幂+任意模数 NTT 即可优化到 O ( n log n ) O(n\log n) O(nlogn)。
这个竟然是纯纯的式子题😭
式子就是:
a n s = ∑ i = 1 n f ( i ) × f ( n − i ) × C n i ans=\sum\limits_{i=1}^{n}{f(i)\times f(n-i)\times C_{n}^{i}} ans=i=1∑nf(i)×f(n−i)×Cni
其中,
f i = 2 C i 2 f_{i}=2^{C_{i}^{2}} fi=2Ci2
相当于枚举断点,然后由于还有一个方向,所以,两遍是对的,就做完了。
正解
cpp
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int N = 100005;
int n;
int fac[N], inv[N];
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int C(int n, int m){
if (n < 0 || m < 0 || n < m){
return 0;
}
return fac[n] * inv[n - m] % mod * inv[m] % mod;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
fac[0] = 1;
for (int i = 1; i < N; i++){
fac[i] = fac[i - 1] * i % mod;
}
inv[N - 1] = qpow(fac[N - 1], mod - 2);
for (int i = N - 2; i >= 0; i--){
inv[i] = inv[i + 1] * (i + 1) % mod;
}
int ans = 0;
for (int i = 1; i <= n; i++){
long long e1 = i * (i - 1) / 2;
long long e2 = (n - i) * (n - i - 1) / 2;
ans = (ans + qpow(2, e1) * qpow(2, e2) % mod * C(n, i) % mod) % mod;
}
cout << ans;
}
QOJ20027 Longest Common Prefix
分析
那就是字典树在线维护即可。然后,cnt 就是经过的个数,就是 pos 的相关。好的,你做完了!
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500005;
int n, ch[N][26], tot;
int de[N], dp[N];
int ans;
int cnt[N];
void insert(string s){
int u = 0;
for (int i = 0; i < (int)s.length(); i++){
if (!ch[u][s[i] - 'a']){
ch[u][s[i] - 'a'] = ++tot;
de[tot] = de[u] + 1;
}
u = ch[u][s[i] - 'a'];
int tmp = cnt[u];
cnt[u] = tmp + 1;
int pos = tmp + 1;
if (de[u] > dp[pos]){
int tmp2 = dp[pos];
dp[pos] = de[u];
ans += (dp[pos] ^ pos) - (tmp2 ^ pos);
}
}
}
string s;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> s;
ans += i;
insert(s);
cout << ans << '\n';
}
}
/*
4
a
ab
abc
abcd
*/
/*
8
ab
b
ba
ab
aa
ba
a
bb
*/
LGP5666 CSP-S 2019 树的重心
原题链接:CSP-S 2019 树的重心
分析
我是不是会写 O ( n 2 ) O(n^2) O(n2)?要不要写一下呢?呃......不手懒了,写!呃......卡宕机了😭
哦,其实只需要在 dfs 时,循环外面进行计算即可,这波躺了😭
考虑怎么换根......
我会树上倍增求 LCA,但是,这个该怎么求呢?我们发现,在换根的时候,子树的大小只会变化 1 1 1!
然后呢?这个时方便换根的......呃......倍增......
那么,我设 d p u , i dp_{u,i} dpu,i 表示以 u u u 为根的子树在......的 2 i 2^i 2i 的情况下的......不知道😭
我不可能直接把子树大小扔进去吧 (*Φ皿Φ*)
我们开始剖分!就是说,我们之前从 CF1174F 中知道,重心一定在重儿子里,那么,直接写一个倍增就可以开始跳了!
原来儿子也可以这样跳吗?
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 300005;
int T;
int n;
vector<int> e[N];
int s[N][20], ss[N], pa[N];
int sz[N];
int ans;
void dfs1(int u, int fa){
sz[u] = 1;
s[u][0] = ss[u] = 0;
pa[u] = fa;
for (auto v : e[u]){
if (v == fa)
continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[s[u][0]]){
ss[u] = s[u][0];
s[u][0] = v;
}
else if (sz[v] > sz[ss[u]]){
ss[u] = v;
}
}
}
void dfs2(int u, int fa){
int son = s[u][0], siz = sz[u];
pa[fa] = u;
for (auto v : e[u]){
if (v == fa)
continue;
if (v == son)
s[u][0] = ss[u];
else
s[u][0] = son;
if (sz[fa] > sz[s[u][0]])
s[u][0] = fa;
for (int i = 1; i <= 19; i++){
s[u][i] = s[s[u][i - 1]][i - 1];
}
sz[u] = n - sz[v];
pa[u] = pa[v] = 0;
int pos = u;
for (int i = 19; i >= 0; i--){
if (sz[u] - sz[s[pos][i]] <= sz[u] / 2)
pos = s[pos][i];
}
ans += pos + pa[pos] * (max(sz[u] - sz[pa[pos]], sz[s[pa[pos]][0]]) <= sz[u] / 2);
pos = v;
for (int i = 19; i >= 0; i--){
if (sz[v] - sz[s[pos][i]] <= sz[v] / 2)
pos = s[pos][i];
}
ans += pos + pa[pos] * (max(sz[v] - sz[pa[pos]], sz[s[pa[pos]][0]]) <= sz[v] / 2);
dfs2(v, u);
}
s[u][0] = son;
sz[u] = siz;
pa[u] = fa;
for (int i = 1; i <= 19; i++){
s[u][i] = s[s[u][i - 1]][i - 1];
}
}
void init(){
for (int i = 1; i <= n; i++){
e[i].clear();
}
ans = 0;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> T;
for (int cs = 1; cs <= T; cs++){
init();
cin >> n;
for (int i = 1, u, v; i < n; i++){
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1, 0);
for (int i = 1; i <= 19; i++){
for (int u = 1; u <= n; u++){
s[u][i] = s[s[u][i - 1]][i - 1];
}
}
dfs2(1, 0);
cout << ans << '\n';
}
}