//題解真心毒瘤,閱讀時請做好心理準備
因爲被審查禁了,重寫一下題解……
顯然知道這是一棵樹,根據貪心的思路,我們儘可能將軍隊向根節點移動,但是不能移動到根節點。
怎麼跑?
要往上走,就得處理這個點以上所有直系父節點
//eg:這棵樹中,的所有直系父節點就是和,的所有直系父節點就是
跑的時候每一個有軍隊的節點向靠近根節點的方向走,直到不能走爲止。如果一個軍隊沒有走到根節點,那麼它顯然只能去看守最後到達的那個節點,這已經是最優方案。接下來我們考慮對於到達根節點的所有點中,我們記錄每個點的兩個屬性:一是他來自哪裏,二是他還能走多長時間的路。接下來我們把剩餘到達根節點的點按照剩餘路程從小到大排序,記爲數組,把根節點的葉子節點中沒有被覆蓋的節點取出,並且按照到達根節點的距離進行從小到大排序,記爲數組。最後我們把兩個數組進行歸併,用中小的去覆蓋中小的。
貪心部分完成
好像還是不明白怎麼解…
求最小時間可以二分答案,如果B完全覆蓋,則時間可行,否則時間不可行
不錯
怎麼實現?
處理直系父節點:DFS找出第二層的兒子們,把他們的第二層都記爲這個點。
處理號節點到各個節點的距離和第二層節點的子節點到第二層節點的距離
各位沒吐吧…
上代碼
#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;
}
寫了三天,一臉懵逼…