main
黄海 2 years ago
parent 9a0b962312
commit bbb6826a45

@ -2,68 +2,53 @@
using namespace std;
#define int long long
#define endl "\n"
const int N = 100010;
const int N = 100010, M = N << 1;
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int f[N], g[N];
int n, mod;
int pro1[N], pro2[N];
void dfs1(int u, int fa) {
f[u] = 1;
vector<int> edgeson;
int n, m;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == fa) continue;
dfs1(v, u);
vector<int> s[N];
int dp[N], pd[N];
f[u] = f[u] * (f[v] + 1) % mod;
edgeson.push_back(v); // 将子节点加入集合,方便之后操作
void dfs1(int x, int fa) {
for (int i : s[x]) {
if (i != fa) {
dfs1(i, x);
dp[x] = dp[x] * (dp[i] + 1) % m;
}
}
int pre1 = 1;
int pre2 = 1;
for (int i = 0; i < edgeson.size(); i++) {
pro1[edgeson[i]] = pre1;
pre1 = pre1 * (f[edgeson[i]] + 1) % mod;
} // 预处理前缀积
for (int i = edgeson.size() - 1; i >= 0; i--) {
pro2[edgeson[i]] = pre2;
pre2 = pre2 * (f[edgeson[i]] + 1) % mod;
} // 预处理后缀积
}
void dfs2(int u, int fa) {
if (fa == 0)
g[u] = 1; // 特判根节点
else
g[u] = (g[fa] * (pro1[u] * pro2[u] % mod) % mod + 1) % mod;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == fa) continue;
dfs2(v, u);
}
void dfs2(int x, int fa) {
int t = pd[x];
for (int i : s[x])
if (i != fa)
pd[i] = pd[i] * t % m;
t = 1;
for (int i : s[x])
if (i != fa)
pd[i] = pd[i] * t % m, t = t * (dp[i] + 1) % m;
t = 1;
reverse(s[x].begin(), s[x].end());
for (int i : s[x])
if (i != fa)
pd[i] = pd[i] * t % m, t = t * (dp[i] + 1) % m;
for (int i : s[x])
if (i != fa) {
++pd[i];
dfs2(i, x);
}
}
signed main() {
// 初始化链式前向星
memset(h, -1, sizeof h);
cin >> n >> mod;
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) dp[i] = pd[i] = 1;
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
s[a].push_back(b), s[b].push_back(a);
}
dfs1(1, 0);
dfs2(1, 0);
for (int i = 1; i <= n; i++) cout << f[i] * g[i] % mod << endl;
for (int i = 1; i <= n; ++i) cout << dp[i] * pd[i] % m << endl;
}

@ -1269,7 +1269,57 @@ signed main() {
}
```
#### [$V-Subtree$](https://www.luogu.com.cn/problem/AT_dp_v)
https://blog.csdn.net/Emm_Titan/article/details/123875298
**题意**
给定一棵 $N$ 个节点的树,现在需要将每一个节点染成黑色或白色。
对于每一个节点 $i$,求强制把第 $i$ 节点染成黑色的情况下,所有的黑色节点组成一个联通块的染色方案数,答案对 $M$ 取模。
**分析**
树上求方案数,而且要求每一个顶点对应的方案数,还是在 $AT$ 的 $DP$ 列表里的,考虑进行换根$DP$。
**求解**
第一步,先求出以第 $i$ 个点为根的子树中,根节点是黑色的黑连通块数量,就是一个普通的树上 $DP$,状态从子节点转移,结果储存在
$dp[i]$ 里。
```cpp {.line-numbers}
void DP(int x,int fa){
for(int i:s[x]){
if(i!=fa){
DP(i,x);
dp[x]=dp[x]*(dp[i]+1)%m;//子树全白也是一种情况
}
}
}
```
第二步,求出第 $i$ 个节点通过它的父亲节点,与此节点的兄弟节点们及其子孙节点和此节点的祖先节点们,所能构成的黑连通块的总数,结果存在 $pd[i]$ 里。
这里有换根的思想。首先它的父亲肯定是黑的,这时把它的父亲节点视作根节点,把它自己及其子树删去,和第一步同样的方法求解。
```cpp {.line-numbers}
void PD(int x,int fa){
Int t=pd[x];
for(int i:s[x])//它的祖先们转移过来的
if(i!=fa)
pd[i]=pd[i]*t%m;
t=1;
for(int i:s[x])//它左侧的兄弟们转移过来的
if(i!=fa)
pd[i]=pd[i]*t%m,t=t*(dp[i]+1)%m;
t=1;
std::reverse(s[x].begin(),s[x].end());
for(int i:s[x])//它右侧的兄弟们转移过来的
if(i!=fa)
pd[i]=pd[i]*t%m,t=t*(dp[i]+1)%m;
for(int i:s[x])if(i!=fa){
++pd[i];
PD(i,x);
}
}
```
显而易见的,步骤一和步骤二中的答案互不相干,可以让节点上方随意排布,节点下方随意排布。根据乘法原理,答案即为
$dp[i] \times pd[i]$ 。
#### [$AcWing$ $1148$ 秘密的牛奶运输](https://www.cnblogs.com/littlehb/p/16054005.html)

Loading…
Cancel
Save