ACWing 264 P4149 [IOI2011]Race

说在前面

还以为是什么神仙题...然后读了一下题发现很可做,但是有点卡。编号从0开始没注意,TLE+MLE一堆

题意简述

给一个\(n\)个节点的带权树和一个正整数\(k\),求一个简单路径,使得边权和等于\(k\),同时边数最少,输出最小边数。数据范围见题。

简单口胡

一看见题目就知道是点分治,然后考虑在点分治的基础上来维护最小值,发现在更新答案的时候,正常的点分治都是在前面的子树中找是否有能与它匹配起来和为\(k\)的。那么我们现在就改成:和为\(k\)中边数最小的。用\(Min[d]\)代表边权和为\(d\)时当前的最短边个数。初始化除\(Min[0] = 0\)其余均为\(+\infty\)。分别求出每个点到当前root的边权和和深度。
对于更新答案:
\(ans = \min(ans,dep[i] + Min[k - dis[i]])\)
对于更新\(Min\)
\(Min[dis[i]] = \min(Min[dis[i]],dep[i])\)

同时在搜索时加上必要剪枝。

# include <bits/stdc++.h>
using namespace std;

# define int long long

const int N = 2e5 + 5;
const int K = 1e6 + 5,inf = 1e10;

int n,k;
struct edge
{
    int v,w;
    edge() {}
    edge(int _v,int _w) : v(_v),w(_w) {}
};

vector <edge> g[N];

int siz[N],max_siz[N];
int Min[K];
// int Minn[K];
bool del[N];
int dep[N],dis[N];
int root = 0,S;
int cnt = 0;
int tmp[N];
int ans;

void find_root(int x,int fa)
{
    siz[x] = 1,max_siz[x] = 0;
    for(int i = 0; i < (int)g[x].size(); i++)
    {
        int v = g[x][i].v;
        if(v == fa || del[v]) continue;
        find_root(v,x);
        siz[x] += siz[v];
        if(siz[v] > max_siz[x]) max_siz[x] = siz[v];
    }
    max_siz[x] = max(max_siz[x],S - max_siz[x]); 
    if(max_siz[x] < max_siz[root]) root = x;
    return;
}

void Get_dis(int x,int fa)
{
    if(dis[x] <= k && dep[x] <= ans) tmp[++cnt] = x;
    if(dep[x] >= ans) return;
    for(int i = 0; i < (int)g[x].size(); i++)
    {
        int v = g[x][i].v;
        if(v == fa || del[v]) continue;
        dis[v] = dis[x] + g[x][i].w;
        dep[v] = dep[x] + 1;
        Get_dis(v,x);
    }
    return;
}

void solve(int x)
{
    queue <int> q;
    for(int i = 0; i < (int)g[x].size(); i++)
    {
        int v = g[x][i].v;
        if(del[v]) continue;
        dis[v] = g[x][i].w;
        dep[v] = 1;
        cnt = 0;
        Get_dis(v,x);
        for(int j = 1; j <= cnt; j++)
        {
            ans = min(ans,dep[tmp[j]] + Min[k - dis[tmp[j]]]);
        }
        for(int j = 1; j <= cnt; j++)
        {
            Min[dis[tmp[j]]] = min(Min[dis[tmp[j]]],dep[tmp[j]]); // Min[dis] = dep
            q.push(tmp[j]);
        }
    }
    while(!q.empty())
    {
        int top = q.front();
        Min[dis[top]] = inf;
        Min[k - dep[top]] = inf;
        q.pop();
    }
    return;
}
void dfs(int x)
{
    // printf("dfs : x = %lld\n",x);
    del[x] = 1;
    Min[0] = 0;
    dep[x] = 0;
    solve(x);
    // printf("YES:solve\n");
    // for(int i = 1; i <= k; i++) Min[i] = inf;
    for(int i = 0; i < (int)g[x].size(); i++)
    {
        int v = g[x][i].v;
        if(del[v]) continue;
        max_siz[root = 0] = S = siz[v];
        find_root(v,0);
        find_root(root,0);
        dfs(root);
    }
    return;
}

signed main(void)
{
    scanf("%lld%lld",&n,&k);
    for(int i = 1; i < n; i++)
    {
        int u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        ++u,++v;
        g[u].push_back(edge(v,w));
        g[v].push_back(edge(u,w));
    }
    max_siz[root = 0] = S = n;
    find_root(1,0);
    find_root(root,0);
    ans = inf * 10;
    for(int i = 1; i <= k; i++) Min[i] = inf;
    dfs(root);
    printf("%lld\n",ans <= n ? ans : -1);
    return 0;
}
posted @ 2021-05-13 22:53  luyiming123  阅读(26)  评论(0编辑  收藏  举报