[P3523]DYN-Dynamite(二分+树形dp)

【原题】

题目描述

The Byteotian Cave is composed of nnn chambers and n−1n-1n−1 corridors that connect them. For every pair of chambers there is unique way to move from one of them to another without leaving the cave.

Dynamite charges are set up in certain chambers.

A fuse is laid along every corridor.

In every chamber the fuses from the adjacent corridors meet at one point, and are further connected to the dynamite charge if there is one in the chamber. It takes exactly one unit of time for the fuse between two neighbouring chambers to burn, and the dynamite charge explodes in the instant that fire reaches the chamber it is inside.

We would like to light the fuses in some mmm chambers (at the joints of fuses) in such a way that all the dynamite charges explode in the shortest time possible since the fuses are lit. Write a program that will determine the minimum such time possible.

给一棵树,书上有一些关键节点,要求你选m个点,使得关键节点到这些点中距离的最小值的最大值最小,求这个值

输入格式

The first line of the standard input holds two integers nnn and mmm(1≤m≤n≤300 0001\le m\le n\le 300\ 0001≤m≤n≤300 000), separated by a single space, that denote, respectively, the number of chambers in the cave and the number of chambers in which fire can be set to the fuses.

The chambers are numbered from 1 to nnn.

The next line contains nnn integers d1,d2,⋯ ,dnd_1,d_2,\cdots,d_nd1,d2,⋯,dn (di∈{0,1}d_i\in {0,1}di∈{0,1}), separated by single spaces.

If di=1d_i=1di=1, then there is dynamite in the iii-th chamber, and if di=0d_i=0di=0, there is none.The following n−1n-1n−1 lines specify the corridors of the cave. Each of them holds two integers a,ba,ba,b(1≤a<b≤n1\le a<b\le n1≤a<b≤n), separated by a single space, denoting that there is a corridor connecting the chambers aaa and bbb. Every corridor appears exactly once in the description.

You may assume that in tests worth 10% of the points it holds additionally that n≤10n\le 10n≤10 , while in tests worth 40% of the points it holds that n≤1 000n\le 1\ 000n≤1 000

输出格式

The first and only line of the standard output should hold a single integer, equal to the minimum time it takes from lighting the fuses to the explosion of all the charges.

输入输出样例

输入 #1

7 2
1 0 1 1 0 1 1
1 3
2 3
3 4
4 5
5 6
5 7

输出 #1

1

说明/提示

给一棵树,书上有一些关键节点,要求你选m个点,使得关键节点到这些点中距离的最小值的最大值最小,求这个值

【思路】

二分答案,考虑是否可行。

选择至多\(m\)个点, 将关键节点(\(a[i] == 1\))覆盖, 使得所有关键节点到最近的选择点的最大值最小(关键节点可以被选中)。

k值为二分的mid值, num值为选择的点个数。

\(f1[u]\)表示u的子树中最远的未覆盖, \(f1[u] = -inf\) 表示该点已经被覆盖。

\(f2[u]\) 表示u的子树中最近的选择点。

\(\begin{cases} f1[u] = max(f1[u], f1[v] + 1);\\f2[u] = min(f2[u], f2[v] + 1); \end{cases}%\)

考虑当前点是否已经被覆盖 / 需要被选择,考虑三种情况:

  1. 当前节点是关键节点,最近的已选择 > k ,无法被覆盖, 交由父节点处理(如果着急将这个点选择,num值不是最小, 出于贪心,先不选择)。 \(f1[u] = max(0, f1[u])\)
  2. 子树最远的未覆盖 + 子树最近的已选择 < k,整个子树可以被覆盖。\((f1[u] + f2[u] <= k) f1[u] = -inf\)
  3. 子树最远的未覆盖 == k, 必须将该点选择,否则必不符合所有点到最近关键点的最大值 <= k。$ f1[u] = -inf, f2[u] = 0$
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <list>
#include <map>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define LL long long
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f
#define PI 3.1415926535898
#define F first
#define S second
#define endl '\n'
#define lson  rt << 1
#define rson  rt << 1 | 1
#define f(x, y, z) for (int x = (y), __ = (z); x < __; ++x)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
using namespace std;

const int maxn = 3e6 + 7;
int n, m;
int a[maxn], f1[maxn], f2[maxn], num;
struct pp
{
    int v, w, next;
}edge[2 * maxn]; 
int head[maxn], cnt;
void add(int t1, int t2, int t3)
{
    edge[++cnt].v = t2;
    edge[cnt].w = t3;
    edge[cnt].next = head[t1];
    head[t1] = cnt;
}

void dfs(int u, int f, int k)
{
    f1[u] = -inf, f2[u] = inf;
    for (int i = head[u]; i; i = edge[i].next)
    {
        int v = edge[i].v;
        if (v == f) continue;
        dfs(v, u, k);
        f1[u] = max(f1[u], f1[v] + 1);
        f2[u] = min(f2[u], f2[v] + 1);
    }
    if (a[u] && f2[u] > k) f1[u] = max(0, f1[u]);//无法被子树的点覆盖 交给父节点处理 
    if (f1[u] + f2[u] <= k) f1[u] = -inf;//子树可被覆盖
    if (f1[u] == k)//必须作为覆盖点
    {
        f1[u] = -inf, f2[u] = 0;
        num++;
    }
    //不能用else if
}
int check(int v)
{
    num = 0;
    dfs(1, -1, v);
    if (f1[1] >= 0) num++; //考虑根节点无法被覆盖的情况
    return num <= m;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    _rep(i, 1, n) cin >> a[i];
    int ta, tb;
    _rep(i, 1, n - 1)
    {
        cin >> ta >> tb;
        add(ta, tb, 1);
        add(tb, ta, 1);
    }
    int l = 0, r = n;
    while (l + 1 < r)
    {
        int mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    if (check(l)) r = l;//不加这句会有一个点过不了
    cout << r << endl;
}

猜你喜欢

转载自www.cnblogs.com/hfcdyp/p/13397703.html
今日推荐