8.24 模拟赛 T3
题意:
给定一棵 \(n\) 个结点,以 \(1\) 为根的树,每个结点有点权 \(a_i\)。可以将若干个点染成红色。定义一个结点 \(u\) 是好的,当且仅当:
- \(u\) 是红色。
- \(u\) 子树内(包括自身)有 \(k\) 个权值和 \(u\) 权值相等红色结点,其中 \(k\) 为给定值。
对于所有的 \(1 \le i \le n\),求出树中有 \(i\) 个好的结点的染色方案。
认为两种方案不同,当且仅当存在一个结点在一个方案中是红色,另一个方案中没有染色。
\(1 \le k \le n \le 3000\),答案对 \(10^9+7\) 取模。
一眼的题,为什么场上写挂了。
首先可以观察到各种权值之间是独立的。也就是说,我们可以分开求出各种颜色的方案,然后背包背起来。
这里分开求方案,可以建虚树,在虚树上 DP 保证复杂度。
然后观察到一个性质:如果一个结点 \(u\) 是好的,那么它的一个红色祖先一定也是好的。
然后就可以 DP 方案数:
- \(f_{u, i}\) 表示考虑以 \(u\) 为根的子树内,有 \(i\) 个好的结点。
- \(g_{u, i}\) 表示考虑以 \(u\) 为根的子树内,有 \(i\) 个红色结点并且强制要求没有好的结点。注意,强制要求没有好的结点并不等价于 \(i \le k\)。
建了虚树,转移略微困难。
虚树上的点分为关键点和辅助点。对于关键点,自然是可以控制染红或者不染红的。但是辅助点只能不染红,辅助点的作用是合并子结点的状态,并不是用来贡献新的方案数的。
所以分类讨论一个结点是否被染红。
- 不染红:
暴力背包,把子结点的 \(f\) 和 \(g\) 都背进来就行了。
这里不会影响 \(g\) 的【强制没有好的结点的性质】,因为 \(u\) 没有被染色,自然不是好的。 - 染红:
多分几步。
首先背包的初始状态是,\(f_{u,0}=1,g_{u,1}=1\)。
接着,仍然是暴力背包合并。
合并完之后注意,\(u\) 可能是一个好的结点。
如果儿子里面有好的结点,自然 \(u\) 是好的,所以可以对 \(f_{u, i},i \ge 2\) 部分做右移操作,即 \(f_{u,i}\gets f_{u, i-1}\)。
对于儿子里面没有好的结点的情况,就用 \(g\) 判断。不难得到 \(f_{u, 1} = \sum_{i=k}g_{u,i}\),再消除掉这部分的 \(g\) 的贡献。
这里有个细节,染红那一块要单独开数组 DP 防止干扰。
最后注意 \(f_{u,0}\) 的赋值,依据状态,\(f_{u,0}=\sum_{i=0}g_{u,i}\)。
答案就是所有颜色的 \(f_{1, i}\) 背起来。
总时间复杂度 \(O(n^2)\),赛时代码改。
#include <bits/stdc++.h>
using namespace std;
//#define filename "occur"
#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 = 3002;
const int P = 1000000007;
void vadd(int &a, int b) { a += b; if(a >= P) a -= P; }
int n, k, L, R;
int a[N];
vector<int> G[N];
struct LCA {
int sz[N], dep[N], son[N], par[N];
void DFS1(int u, int fa) {
sz[u] = 1, dep[u] = dep[fa] + 1, par[u] = fa;
for(auto v : G[u]) if(v != fa) {
DFS1(v, u);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
int top[N];
void DFS2(int u, int t) {
top[u] = t;
if(son[u]) DFS2(son[u], t);
for(auto v : G[u]) if(v != par[u] && v != son[u]) DFS2(v, v);
}
int getLCA(int u, int v) {
while(top[u] != top[v]) {
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = par[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
} C;
int dfn[N], num;
void get_dfn(int u, int fa) {
dfn[u] = ++num;
for(auto v : G[u]) if(v != fa) get_dfn(v, u);
}
vector<int> vt[N]; //虚树
vector<int> vec, p;
int mark[N];
void build() {
auto cmp = [&] (int x, int y) { return dfn[x] < dfn[y]; };
sort(all(vec), cmp);
clear(p);
upw(i, 1, (int)vec.size() - 1) p.push_back(C.getLCA(vec[i], vec[i-1]));
for(auto u : p) mark[u] = 0;
p.push_back(1), mark[1] = 0;
for(auto u : vec) p.push_back(u), mark[u] = 1;
sort(all(p), cmp), p.erase(unique(all(p)), p.end());
upw(i, 1, (int)p.size() - 1) {
int u = p[i], v = C.getLCA(p[i], p[i-1]);
vt[u].push_back(v), vt[v].push_back(u);
}
}
int f[N][N], g[N][N];
int sz[N];
void DFS(int u, int fa) {
f[u][0] = g[u][0] = 1;
sz[u] = 1;
for(auto v : vt[u]) if(v != fa) {
DFS(v, u);
vector<int> tg(sz[u] + sz[v] + 1);
upw(i, 0, sz[u]) upw(j, 0, sz[v]) vadd(tg[i + j], 1ll * g[u][i] * g[v][j] % P);
upw(i, 0, sz[u] + sz[v]) g[u][i] = tg[i];
vector<int> tf(sz[u] + sz[v] + 1);
upw(i, 0, sz[u]) upw(j, 0, sz[v]) vadd(tf[i + j], 1ll * f[u][i] * f[v][j] % P);
upw(i, 0, sz[u] + sz[v]) f[u][i] = tf[i];
sz[u] += sz[v];
}
if(mark[u]) { //u点染红的方案数,加法原理加起来
vector<int> tmpf(n + 1), tmpg(n + 1);
tmpf[0] = tmpg[1] = 1;
sz[u] = 1;
for(auto v : vt[u]) if(v != fa) {
vector<int> tg(n + 1);
upw(i, 0, sz[u]) upw(j, 0, sz[v]) vadd(tg[i + j], 1ll * tmpg[i] * g[v][j] % P);
upw(i, 0, sz[u] + sz[v]) tmpg[i] = tg[i];
vector<int> tf(n + 1);
upw(i, 0, sz[u]) upw(j, 0, sz[v]) vadd(tf[i + j], 1ll * tmpf[i] * f[v][j] % P);
upw(i, 0, sz[u] + sz[v]) tmpf[i] = tf[i];
sz[u] += sz[v];
}
dnw(i, sz[u], 2) tmpf[i] = tmpf[i-1];
tmpf[1] = 0;
upw(i, k, sz[u]) vadd(tmpf[1], tmpg[i]);
upw(i, 1, sz[u]) vadd(f[u][i], tmpf[i]);
upw(i, 1, min(sz[u], k-1)) vadd(g[u][i], tmpg[i]);
}
f[u][0] = 0;
upw(i, 0, sz[u]) vadd(f[u][0], g[u][i]);
}
int h[N << 1]; //背包背的时候,可能会2倍
void WaterM() {
cin >> n >> L >> R >> k;
upw(i, 1, n) cin >> a[i];
vector<int> va{-1};
upw(i, 1, n) va.push_back(a[i]);
sort(all(va)), va.erase(unique(all(va)), va.end());
upw(i, 1, n) a[i] = lower_bound(all(va), a[i]) - va.begin();
int ctot = (int)va.size() - 1;
upw(i, 2, n) {
int fa;
cin >> fa;
G[fa].push_back(i), G[i].push_back(fa);
}
C.DFS1(1, 0), C.DFS2(1, 1);
get_dfn(1, 0);
int tot = 0;
h[0] = 1;
upw(val, 1, ctot) {
upw(i, 1, n) clear(vt[i]);
clear(vec);
upw(i, 1, n) if(a[i] == val) vec.push_back(i);
build();
DFS(1, 0);
vector<int> t(tot + sz[1] + 1);
upw(i, 0, tot) upw(j, 0, sz[1]) vadd(t[i + j], 1ll * h[i] * f[1][j] % P);
upw(i, 0, tot + sz[1]) h[i] = t[i];
tot += sz[1];
for(auto i : p) upw(j, 0, sz[i]) f[i][j] = g[i][j] = 0;
}
int ans = 0;
upw(i, L, R) vadd(ans, h[i]);
cout << ans << '\n';
}
signed main() {
#ifdef filename
FileOperations();
#endif
signed _ = 1;
#ifdef multi_cases
scanf("%d", &_);
#endif
while(_--) WaterM();
return 0;
}

浙公网安备 33010602011771号