P6843 [BalticOI 2015] DFS

题意

传送门 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 rootu 的路径长度。对于叶子节点 u u u若不添加边,则满足条件时有 d s [ u ] = K ds[u]=K ds[u]=K

若添加边且只经过这条边一次,设其为 p → q p\rightarrow q pq,则有
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 rtu 长度为 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])(Kds[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;
}

Guess you like

Origin blog.csdn.net/neweryyy/article/details/120071526