NOIP 2012 疫情控制

题目链接

https://www.luogu.org/problemnew/show/P1084

这个很考码力……

写得我………………

大致思路

一般关于时间都有一个隐含的最大值

然后又是最小时间

所以就是最大值最小,二分答案

二分出时间后

就让每个军队尽量往上跳

跳不到根节点就停在那了

如果跳的到根节点,就记录到根节点还能再跳多少,按照这个值排序

然后把还没被控制的子树记录一下

然后按照距离排序

然后就贪心,就是让路程剩余值最小的去覆盖子树距离最小的,这样不会浪费,

留下剩余值大的去覆盖子树距离大的

注意这个时候如果子树在该点跳到根节点的路径时,就直接跳回去

关于二分答案的思路

一般题目是从条件出发,当达到题目要求的时候可以推出答案。

而二分答案就是从答案出发,利用条件来判断看可不可以达到题目要求。

思维一定要换过来。

一些细节

(1)写递归函数的时候,不要下意识的写dfs,比如这道题的push_up函数往下的时候我就写成dfs了,而且我静态差错还没查出来

(2)这道题可以设1的根节点为0,向上跳的时候注意判断

(3)注意大于和不等于的区别

(4)判断是否是叶子节点可以用一个变量来表示,不需要记录度数

(5)二分中的check函数因为要执行很多次所以要记得初始化

(6)写结构体的时候记得内部加分号,每次都不加……

(7)这道题注意long long(不用也能过??)

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int MAXN = 5e5 + 10;
const int MAXM = 20;

struct Edge{ int to, w, next; };
Edge e[MAXN << 1];
int head[MAXN], tot;

ll dis[MAXN][MAXM + 5];
int f[MAXN][MAXM + 5], st[MAXN];
int vis[MAXN], n, m, cnt; 

void AddEdge(int from, int to, int w)
{
    e[tot] = Edge{to, w, head[from]};
    head[from] = tot++;
}

void read(int& x)
{
    int f = 1; x = 0; char ch = getchar();
    while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }
    while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
    x *= f;
}

void dfs(int u, int fa)
{
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(v == fa) continue;
        dfs(v, u);
        f[v][0] = u;
        dis[v][0] = e[i].w;
    }
}

void push_up(int u, int fa)
{
    bool ok = 1, p = 0;
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(v == fa) continue;
        push_up(v, u);
        if(!vis[v]) ok = 0;
        p = 1;
    }
    if(u != 1 && ok && p) vis[u] = 1;
} 

struct node
{ 
    int id; ll w; 
    bool operator < (const node& rhs) const
    {
        return w < rhs.w;
    }
};
vector<node> v1;
vector<node> v2;

bool check(ll ans)
{
    memset(vis, 0, sizeof(vis));
    v1.clear(); v2.clear();
    
    _for(i, 1, m)
    {
        int u = st[i];
        ll dist = 0;
        for(int j = MAXM; j >= 0; j--)
            if(f[u][j] && dist + dis[u][j] <= ans)
                dist += dis[u][j], u = f[u][j];
                
        if(u != 1) vis[u] = 1;
        else
        {
            u = st[i];
            for(int j = MAXM; j >= 0; j--)
                if(f[u][j] > 1) //这里是大于不是不等于 
                    u = f[u][j];
            v1.push_back(node{u, ans - dist});
        }
    }
    
    push_up(1, 0);
    for(int i = head[1]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(!vis[v]) v2.push_back(node{v, e[i].w}); 
    } 
    
    sort(v1.begin(), v1.end());
    sort(v2.begin(), v2.end());
    
    int j = 0;
    REP(i, 0, v1.size())
    {
        if(!vis[v1[i].id]) vis[v1[i].id] = 1;
        else if(v1[i].w >= v2[j].w) vis[v2[j].id] = 1;
        while(j < v2.size() && vis[v2[j].id]) j++;
        if(j == v2.size()) return true;
    }
    return false;
}

int main()
{
    ll l = -1, r = 0;
    memset(head, -1, sizeof(head)); tot = 0;

    read(n);
    REP(i, 1, n)
    {
        int u, v, w; read(u); read(v), read(w);
        AddEdge(u, v, w); AddEdge(v, u, w);
        if(u == 1 || v == 1) cnt++;
        r += w;
    }
    
    dfs(1, 0);
    f[1][0] = 0;
    _for(j, 1, MAXM)
        _for(u, 1, n)
        {
            f[u][j] = f[f[u][j-1]][j-1];
            dis[u][j] = dis[u][j-1] + dis[f[u][j-1]][j-1];
        }
    
    read(m);
    _for(i, 1, m) read(st[i]);
    if(m < cnt) { puts("-1"); return 0; }
    
    while(l + 1 < r)
    {
        int m = (l + r) >> 1;
        if(check(m)) r = m;
        else l = m;
    }
    printf("%d\n", r);
    
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/sugewud/p/9879637.html