[NOIP] [LCA] [貪心] NOIP2012Day2 疫情控制(blockade)

//題解真心毒瘤,閱讀時請做好心理準備

題目傳送門

因爲被審查禁了,重寫一下題解……
顯然知道這是一棵樹,根據貪心的思路,我們儘可能將軍隊向根節點移動,但是不能移動到根節點。
怎麼跑?
要往上走,就得處理這個點以上所有直系父節點
這裏寫圖片描述
//eg:這棵樹中,55的所有直系父節點就是221166的所有直系父節點就是3,13,1

跑的時候每一個有軍隊的節點向靠近根節點的方向走,直到不能走爲止。如果一個軍隊沒有走到根節點,那麼它顯然只能去看守最後到達的那個節點,這已經是最優方案。接下來我們考慮對於到達根節點的所有點中,我們記錄每個點的兩個屬性:一是他來自哪裏,二是他還能走多長時間的路。接下來我們把剩餘到達根節點的點按照剩餘路程從小到大排序,記爲數組AA,把根節點的葉子節點中沒有被覆蓋的節點取出,並且按照到達根節點的距離進行從小到大排序,記爲數組BB。最後我們把兩個數組進行歸併,用AA中小的去覆蓋BB中小的。
貪心部分完成
好像還是不明白怎麼解…
求最小時間可以二分答案,如果B完全覆蓋,則時間可行,否則時間不可行
不錯
怎麼實現?
處理直系父節點:DFS找出第二層的兒子們,把他們的第二層都記爲這個點。
處理11號節點到各個節點的距離和第二層節點的子節點到第二層節點的距離
各位沒吐吧…
上代碼

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 50010
using namespace std;

struct link
{
    int to;
    long long val;
    int nxt;
};

int n,m;
int u,v;
long long w;
int one_son_num;
long long sum_val;
int army[MAXN];
//Input&Perwork

link e[2*MAXN];
int edge_num[MAXN],cnt;
//Link_Table

int father[MAXN];
long long dis[MAXN];
int second_floor[MAXN];
long long second_to_first[MAXN];
long long leaf_to_second[MAXN];
//DFS

bool vis[MAXN];
long long rest[MAXN];
int cur[MAXN],pre[MAXN];
int match[MAXN];
bool can[MAXN];
//check

bool cmp(int a,int b)
{
    return dis[a]<dis[b];
}

bool cmp1(int a,int b)
{
    return second_to_first[a]>second_to_first[b];
}

bool cmp2(int a,int b)
{
    return a>b;
}

void add(int u, int v, long long w)
{
    e[cnt]=(link){v,w,edge_num[u]}; edge_num[u]=cnt++;
    e[cnt]=(link){u,w,edge_num[v]}; edge_num[v]=cnt++;
}

void dfs1(int node,int nowfather,long long len) //處理每個點到1號節點的距離
{
    father[node]=nowfather;
    dis[node]=len;
    for (int i=edge_num[node];~i;i=e[i].nxt)
        if (e[i].to!=nowfather)
            dfs1(e[i].to,node,len+e[i].val);
}

void dfs2(int node,bool have_branch,int bigfather,long long len)//處理第二層的子節點到第二層的距離,如果只有一個節點,則不處理
{
    second_floor[node]=bigfather;
    if (!have_branch)
    {
        int cntson=0;
        for (int i=edge_num[node];~i;i=e[i].nxt)
            if (e[i].to!=father[node])
            {
                cntson++;
                if (cntson==2)
                {
                    have_branch=true;
                    break;
                }
            }
    }
    leaf_to_second[node]=len;
    for (int i=edge_num[node];~i;i=e[i].nxt)
        if (e[i].to!=father[node])
            if (have_branch) dfs2(e[i].to,have_branch,bigfather,len+e[i].val);
            else dfs2(e[i].to,have_branch,bigfather,0);
}

bool check(long long time)
{
    memset(vis,false,sizeof(vis));//vis表示該點已有軍隊
    for (int i=edge_num[1];~i;i=e[i].nxt)
    {
        can[e[i].to]=false;
        cur[e[i].to]=0;
    }
    rest[0]=0;
    for (int i=1;i<=m;i++)
    {
        if (time>dis[army[i]]) //足夠走到1號節點,可以走到其他第二層
        {
            rest[++rest[0]]=time-dis[army[i]];
            pre[rest[0]]=cur[second_floor[army[i]]];//鏈狀維護
            cur[second_floor[army[i]]]=rest[0];
        }
        else //只能走到自己的第二層
        {
            if (time>=leaf_to_second[army[i]])
                can[second_floor[army[i]]]=true;
        }
    }
    match[0]=0;
    for (int i=edge_num[1];~i;i=e[i].nxt)
        if (!can[e[i].to]) match[++match[0]]=e[i].to;
    if (match[0]>rest[0]) return false;//剩下可去其他第二層節點的軍隊不夠需要軍隊的第二層節點
    sort(match+1,match+match[0]+1,cmp1);//按與1號節點距離從大到小排序
    sort(rest+1,rest+rest[0]+1,cmp2);//按剩餘時間從大到小排序
    for (int i=1,j=1;i<=match[0]&&j<=rest[0];i++)//覆蓋
    {
        while (vis[cur[match[i]]]) cur[match[i]]=pre[cur[match[i]]];//找未覆蓋的軍隊
        if (cur[match[i]]) vis[cur[match[i]]]=true;
        else
        {
            if (rest[j]<second_to_first[match[i]]) return false;//過不去
            vis[j]=true;
        }
        while (vis[j]) j++;
    }
    return true;
}

long long binary()
{
    long long left=0,right=sum_val;
    while (left+1!=right)
    {
        long long middle=(left+right)>>1;
        if (check(middle)) right=middle;
        else left=middle;
    }
    return right;
}

int main()
{
    memset(edge_num,-1,sizeof(edge_num));
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        scanf("%d %d %lld",&u,&v,&w);
        add(u,v,w);sum_val+=w;
    }
    dfs1(1,-1,0);
    for (int i=edge_num[1];~i;i=e[i].nxt)
    {
        one_son_num++;
        second_to_first[e[i].to]=e[i].val;
        dfs2(e[i].to,false,e[i].to,0);
    }
    scanf("%d",&m);
    if (one_son_num>m)
    {
        printf("-1\n");
        return 0;
    }
    for (int i=1;i<=m;i++) scanf("%d",&army[i]);
    sort(army+1,army+m+1,cmp);
    printf("%lld\n",binary());
    return 0;
}

寫了三天,一臉懵逼…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章