タイトル
タイトル
n個のノードとn-1個のエッジのツリーが与えられた場合、ノード1をルートとします
ここで、kノードを工業都市として選択する必要があり、残りの都市はすべて観光都市です
Q. すべての工業都市からは、に行ってきましたルートの渡し観光都市の合計の最大数
問題解決のアイデア
マップ全体がすべて工業都市であるとすると、逆の考え方で、次にnk観光都市を選択する必要があります
ノード1はツリーのルートであるため、ノードごとに、このノードをルートとするサブツリーに含まれるノードの数と、このノードが所有する祖先ノードの数を計算できます。
貪欲によると、私たちが選択する観光都市はすべてルートノードに可能な限り近いことがわかります
ルートノードを観光都市として選択することが最初のステップでなければならないことに疑いの余地はありません。この時点での答えはn-1です。つまり、現時点での工業都市の数です。
例としてサンプル1の写真を撮ります
最初のステップは、観光都市として1を選択することです。答えは、工業都市の数である6です。
次に、2番目のステップ
観光都市として3を選択すると、ノード5とノード6のパスに観光都市が追加されていることがわかります。このときの答えは6-1 + 2 = 7です。
観光都市として4を選択すると、ノード7のパスに観光都市が追加されていることがわかります。このときの答えは6-1 + 1 = 6です。
それ以外の場合、観光都市として2 5 6 7を選択すると、他の工業都市はこれらの観光都市を通過せず、答えは6-1 + 0 = 5になります。
要約すると、次に特定のノードiを観光都市として選択すると、
次に、回答に対する「増加した貢献」は、ノードi(ノードiを除く)をルートとするサブツリーに含まれるノードの数です。
回答に対する「減少した貢献」は、ノードiのすべての祖先ノードにある観光都市の数です
そして、私たちは貪欲なので、答えの最大値を取得するために増加(貢献を増やす)または減少(貢献を減らす)のいずれかを行うことができます
また、ノードがルートノードに近いほど、子ノードの数が多くなり、祖先ノードの数が少なくなるため、確実に優先されます。
したがって、回答の各ノードの寄与は、に変換することができる-ノードIをルートとするサブツリー(ノードを含まないI)ノードの数を含むノードIの祖先ノード数
処理後に整理し、最初からnkを取る
完全なプログラム
ルートノード1の深さを0とする
次に、各ノードの祖先ノードの数が各ノードの深さになります
ノードi(ノードiを除く)をルートとするサブツリーに含まれるノードの数については、dfsを再帰的に追加できます。
(事前テスト:264ms / 2000ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> G[200050];
bool vis[200050];
struct node
{
int cnt,dis; //cnt存子树除该节点外的节点数量,dis存节点深度
bool operator < (const node& a) const
{
return cnt-dis>a.cnt-a.dis; //按照对答案的贡献(cnt-dis)从大到小排序
}
}ar[200050];
int dfs(int pos,int fa)
{
vis[pos]=true; //标记访问
ar[pos].dis=ar[fa].dis+1; //节点深度=父节点节点深度+1
for(int it:G[pos])
if(!vis[it])
ar[pos].cnt+=dfs(it,pos); //求除自己以外子树中节点数量之和
return ar[pos].cnt+1; //返回时要加上自己
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,k,a,b;
cin>>n>>k;
for(int i=1;i<n;i++)
{
cin>>a>>b;
G[a].push_back(b);
G[b].push_back(a);
}
ar[0].dis=-1; //特殊处理下,dis[1]=dis[0]+1=0 -> dis[0]=-1
dfs(1,0);
sort(ar+1,ar+n+1);
ll ans=0;
for(int i=1;i<=n-k;i++)
ans+=ar[i].cnt-ar[i].dis;
cout<<ans<<'\n';
return 0;
}