洛谷 P8867 建造军营
边双直接缩掉,成为一棵树。下面的【结点】都是指缩之后的。
于是可以定义一个 \(a_u\) 为,\(u\) 点内有军营的方案数,\(b_u\) 为无。
总方案数就对应着,树上每种方案的权值和。
首先考虑一个 DP,设 \(f_{u, 0/1}\) 表示 \(u\) 子树内部有或没有军营的权值和,有的话钦定 \(u\) 处必须有军营。
转移的话,定义一个辅助数组 \(sum_u\),表示 \(u\) 子树内部有军营的权值和。但要求,所有军营必须都与 \(u\) 连通。(意思是,如果只有 \(u\) 的一个后代设立的军营,讲道理不看守任何道路都是合法的,但是这里要求这个后代到 \(u\) 的路径上的道路都要看守)
不难发现有方程
\[f_{u, 0} \gets \prod 2f_{v, 0}
\]
\[f_{u, 1} \gets \prod (2f_{v, 0} + sum_v)
\]
\[sum_u \gets f_{u, 1} - f_{u, 0} + \prod (2f_{v, 0} + sum_v)
\]
然后考虑怎么统计答案。
记一个 \(g_u\) 表示所有军营的 LCA 在 \(u\) 的方案数。
这就要求 \(u\) 处有军营,或 \(u\) 处没有军营且至少两个儿子的子树内有军营。
再记一个 \(sumg_u\) 表示 \(u\) 子树内 \(g\) 的和,但是这里 \(g\) 要带上【\(u\) 子树内其他位置不建造的方案数】,即一个权。
然后
\[g_u \gets f_{u,1} + b_u\left [\prod(2f_{v, 0} + sumg_v) - \prod (2f_{v, 0}) - \sum_v sumg_v \cdot \prod_{x \ne v, x \text{ is a son of u}} (2f_{v, 0})\right]
\]
\(sumg\) 的转移有点难描述,不表。看代码。
时间复杂度的话,用手法离线线性求逆元的话是可以做到线性的。不过没必要。\(O(n \log V)\),其中 \(V\) 为值域。
#include <bits/stdc++.h>
using namespace std;
//#define filename "xxx"
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)
//#define multi_cases 1
#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define ull unsigned long long
#define all(v) v.begin(), v.end()
#define upw(i, a, b) for(int i = (a); i <= (b); ++i)
#define dnw(i, a, b) for(int i = (a); i >= (b); --i)
template<class T> bool vmax(T &a, T b) { return b > a ? a = b, true : false; }
template<class T> bool vmin(T &a, T b) { return b < a ? a = b, true : false; }
template<class T> void clear(T &x) { T().swap(x); }
const int N = 1e6+2, M = 1e6+2;
const int P = 1000000007;
void vadd(int &a, int b) { a += b; if(a >= P) a -= P; }
int qpow(int a, int b) {
int res = 1;
while(b) {
if(b & 1) res = 1ll * res * a % P;
b >>= 1;
a = 1ll * a * a % P;
}
return res;
} int inv(int x) { return qpow(x, P-2); }
int n, m;
pii edges[M];
struct edge {
int v, next;
edge(int v = 0, int next = 0) : v(v), next(next) { }
} e[M << 1];
int head[N], etot = 2;
void addedge(int u, int v) { e[etot] = edge(v, head[u]), head[u] = etot++; }
int mark[N];
int dfn[N], low[N], num;
stack<int> st;
int colour[N], ctot;
int a[N], b[N];
void Tarjan(int u, int fa) {
dfn[u] = low[u] = ++num;
st.push(u);
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if(v == fa && mark[u] == (i ^ 1)) continue;
if(!dfn[v]) mark[v] = i, Tarjan(v, u), vmin(low[u], low[v]);
else vmin(low[u], dfn[v]);
}
if(dfn[u] == low[u]) {
++ctot;
while(!st.empty()) {
int x = st.top();
st.pop();
colour[x] = ctot, ++a[ctot];
if(x == u) break;
}
}
}
vector<int> G[N];
int pw[M];
int f[N][2];
int sum[N], sub[N];
int sz[N], coe[N];
void DFS(int u, int fa = 0) {
f[u][1] = a[u];
sum[u] = b[u], sub[u] = b[u];
f[u][0] = b[u];
sz[u] = 1;
for(auto v : G[u]) if(v != fa) {
DFS(v, u);
sz[u] += sz[v];
sub[u] = 1ll * sub[u] * sub[v] % P;
f[u][1] = 1ll * f[u][1] * (2ll * f[v][0] + sum[v]) % P;
sum[u] = 1ll * sum[u] * (2ll * f[v][0] + sum[v]) % P;
f[u][0] = 1ll * f[u][0] * 2 * f[v][0] % P;
}
vadd(sum[u], (f[u][1] - f[u][0] + P) % P);
}
int g[N], sumg[N];
void DFS2(int u, int fa = 0) {
int prod = 1, t = 0;
int s = 1;
for(auto v : G[u]) if(v != fa) {
DFS2(v, u);
s = 2ll * s * f[v][0] % P;
}
for(auto v : G[u]) if(v != fa) {
prod = 1ll * prod * (2ll * f[v][0] + sumg[v]) % P;
vadd(t, sumg[v] * (1ll * s * inv(2 * f[v][0]) % P) % P);
vadd(sumg[u], 1ll * sumg[v] * (1ll * sub[u] * inv(sub[v]) % P * pw[sz[u] - sz[v] - 1] % P) % P);
}
g[u] = (f[u][1] + 1ll * b[u] * (prod - s - t + 2ll * P)) % P;
vadd(sumg[u], g[u]);
}
const int root = 1;
void WaterM() {
cin >> n >> m;
upw(i, 1, m) {
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v), addedge(v, u);
edges[i] = pii(u, v);
}
upw(i, 1, n) if(!dfn[i]) Tarjan(i, 0);
upw(i, 1, m) {
auto [u, v] = edges[i];
if(colour[u] != colour[v]) {
u = colour[u], v = colour[v];
G[u].push_back(v), G[v].push_back(u);
}
else ++b[colour[u]];
}
pw[0] = 1;
upw(i, 1, m) pw[i] = 2 * pw[i-1] % P;
upw(i, 1, ctot) {
int c = a[i], e = b[i];
a[i] = 1ll * (pw[c] - 1) * pw[e] % P;
b[i] = pw[e];
}
// upw(i, 1, n) cerr << colour[i] << ' ';
// cerr << '\n';
// upw(i, 1, ctot) cerr << a[i] << ' ' << b[i] << '\n';
// cerr << '\n';
DFS(root);
int ans = 0, prod = 1;
upw(i, 1, ctot) prod = 1ll * prod * b[i] % P;
upw(i, 1, ctot) coe[i] = 1ll * prod * inv(sub[i]) % P * pw[ctot - sz[i]] % P;
DFS2(root);
upw(i, 1, ctot) vadd(ans, 1ll * g[i] * coe[i] % P);
// upw(i, 1, ctot) cerr << f[i][1] << ' ' << g[i] << ' ' << sumg[i] << '\n';
// upw(i, 1, ctot) cerr << g[i] << " * " << coe[i] << '\n';
cout << ans << '\n';
}
signed main() {
#ifdef filename
FileOperations();
#endif
signed _ = 1;
#ifdef multi_cases
scanf("%d", &_);
#endif
while(_--) WaterM();
return 0;
}

浙公网安备 33010602011771号