题意
传送门 P6843 [BalticOI 2015]File Paths
题解
将’/'看作目录或文件的前缀,与之相连;从上层目录向其放置的目录或文件连一条边,边代表子节点对应的字符串,那么得到一颗 T r i e Trie Trie;仅考虑长度,那么得到一个树形 D A G DAG DAG。问题转化为在一个树形 D A G DAG DAG 中添加至多一条端点非叶子节点的边,求能否找到一条从根节点到各叶子节点的路径,使之权值和为 K K K。
考虑’/’,使 S S S 增一。设 d s [ u ] ds[u] ds[u] 为 r o o t → u root\rightarrow u root→u 的路径长度。对于叶子节点 u u u,若不添加边,则满足条件时有 d s [ u ] = K ds[u]=K ds[u]=K。
若添加边且只经过这条边一次,设其为 p → q p\rightarrow q p→q,则有
d s [ p ] + S + ( d s [ u ] − d s [ q ] ) = K ds[p]+S+(ds[u]-ds[q])=K ds[p]+S+(ds[u]−ds[q])=K 其中 q q q 是 u u u 的祖先节点。那么预处理出所有的 d s [ p ] + S ds[p]+S ds[p]+S 并统计,进行一次 D F S DFS DFS,同时维护栈中的节点。搜索至叶子节点时,栈中节点都是其祖先节点,枚举之,对满足下式的情况记录答案
d s [ p ] + S = K − ( d s [ u ] − d s [ q ] ) ds[p]+S=K-(ds[u]-ds[q]) ds[p]+S=K−(ds[u]−ds[q]) 若添加的边经过不止一次,则有 q q q 为 p p p 或其祖先节点,则 r t → u rt\rightarrow u rt→u 长度为 d s [ u ] + x × ( S + d s [ p ] − d s [ q ] ) = K ds[u]+x\times (S+ds[p]-ds[q])=K ds[u]+x×(S+ds[p]−ds[q])=K 即
( S + d s [ p ] − d s [ q ] ) ∣ ( K − d s [ u ] ) (S+ds[p]-ds[q])\vert (K-ds[u]) (S+ds[p]−ds[q])∣(K−ds[u]) 那么在 D F S DFS DFS 框架下,保证搜索到的叶子节点的祖先节点,都遍历其子树统计 S + d s [ p ] − d s [ q ] S+ds[p]-ds[q] S+ds[p]−ds[q],回溯时将信息删去即可。
上述统计值取值都是 [ 0 , k ] [0,k] [0,k],按照值域记录即可。总时间复杂度 O ( ( N + M ) 2 + M K ) ) O\Big((N+M)^2+M\sqrt K)\Big) O((N+M)2+MK))。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l, _ = r; i < _; ++i)
#define _rep(i, r, l) for (int i = r - 1, _ = l; i >= _; --i)
const int maxn = 6010, maxk = 1000005;
struct edge
{
int to, cost;
};
int N, M, K, S, top, stk[maxn];
int ds[maxn], cnta[maxk], cntb[maxk];
bool res[maxn];
vector<edge> G[maxn];
void pdfs(int u, int d)
{
ds[u] = d;
int t = S + ds[u];
if (u <= N && t <= K)
++cnta[t];
for (auto &e : G[u])
pdfs(e.to, d + e.cost);
}
bool judge(int u)
{
if (ds[u] == K)
return 1;
rep(i, 0, top)
{
int t = K - (ds[u] - ds[stk[i]]);
if (t >= 0 && t <= K && cnta[t])
return 1;
}
if (K - ds[u] > 0)
{
int a = K - ds[u];
for (int i = 1; i * i <= a; ++i)
if (a % i == 0)
if (cntb[i] || cntb[a / i])
return 1;
}
return 0;
}
void add(int u, int rt, int d)
{
int t = S + ds[u] - ds[rt];
if (u <= N && t <= K)
cntb[t] += d;
for (auto &e : G[u])
add(e.to, rt, d);
}
void dfs(int u)
{
if (u > N)
{
res[u] = judge(u);
return;
}
stk[top++] = u;
add(u, u, 1);
for (auto &e : G[u])
dfs(e.to);
add(u, u, -1);
--top;
}
int main()
{
scanf("%d%d%d%d", &N, &M, &K, &S);
++S;
rep(i, 1, N + M + 1)
{
int p, l;
scanf("%d%d", &p, &l);
G[p].push_back(edge{
i, l + 1});
}
pdfs(0, 0), dfs(0);
rep(i, N + 1, N + M + 1) puts(res[i] ? "YES" : "NO");
return 0;
}