

> F. Momoyo and the Network
time limit per test3 seconds
memory limit per test256 megabytes
Where Is That Bustling Marketplace Now— Unconnected Marketeers
The Underground Great Line Network is a grand transit system connecting all corners of Gensokyo. Momoyo noticed that the network's layout resembled a tree∗ structure. She couldn't help but imagine the most effective way to dismantle that tree.
Given a tree with n nodes where node i has weight ai, select a simple path of exactly k edges and remove all edges on it. This splits the tree into k+1 connected components, each with weight equal to the sum of its nodes' weights. You need to maximize the minimum component weight, or output −1 if no simple path of exactly k edges exists.
∗A tree is a connected graph without cycles.
Input
Each test contains multiple test cases. The first line contains the number of test cases t (1≤t≤104). The description of the test cases follows.
The first line of each test case contains two integers n and k (1≤k≤n−1, 2≤n≤2⋅105).
The second line contains n integers, where the i-th integer represents ai (1≤ai≤109).
The next n−1 lines each contain two integers u and v, representing an edge of the tree.
It is guaranteed that the sum of n over all test cases does not exceed 2⋅105.
Output
> For each test case, output the maximum possible minimum component weight, or −1 if no such path exists.
cpp
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
long long arr[200001];
vector<vector<int>> edge;
vector<bool> visit;
vector<long long> edge_v,edge_min,edge_len;
long long n, k, t,edge_total=0;
bool dfs_find(int idx,long long mid)
{
visit[idx] = true;
edge_len[idx] = 0;
vector<pair<long long, long long>> edges;
for (int i : edge[idx])
{
if (visit[i])continue;
if (dfs_find(i, mid)) return true;
if (mid <= edge_v[i])
{
edges.push_back({ edge_v[i], edge_len[i] });
}
if (edge_min[i] >= mid) {
edge_len[idx] = max(edge_len[i] + 1, edge_len[idx]);
}
if (edge_len[i] + 1 >= k && edge_total - edge_v[i] >= mid && edge_v[i] >= mid) {
//자를 수 있음.
return true;
}
}
sort(edges.begin(), edges.end());
int ii= -1;
int l =-1, r = -1;
for (int j = (int)edges.size() - 1; j >= 0; j--) {
while (ii + 1 < edges.size() && edge_total- edges[j].first - edges[ii + 1].first >= mid) {
ii++;
if (edges[ii].second > l) {//l값이 크고 r값이 작음
r =l;
l = edges[ii].second;
}
else if (edges[ii].second > r) {
r = edges[ii].second;
}
//이 과정이 잘 이해가 안갔는데 결국 k 값 이상되는 길이를 찾는 과정임.
}
if (ii == -1) {
continue;
}
if (edge_total - 2 * edges[j].first >= mid &&l== edges[j].second) {
//어 지금 제일 큰 길이와 동일하면 r을 써야함
if (r + edges[j].second + 2 >= k) {
return true;
}
}
else {
if (l + edges[j].second + 2 >= k) {
return true;
}
}
}
return false;
}
void dfs(int idx)
{
visit[idx] = true;
edge_v[idx] = arr[idx];
vector<int> e;
for (int i : edge[idx])
{
if (visit[i])continue;
dfs(i);
edge_v[idx] += edge_v[i];
e.push_back(i);
}
for (int i:e)
{
edge_min[i] = min(edge_v[idx] - edge_v[i], edge_v[i]);
}
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int u,v;
cin >> t;
for (int a = 0; a < t; a++)
{
cin >> n >> k;
edge.assign(n, {});
edge_v.assign(n,0);
edge_min.assign(n, 0);
edge_len.assign(n, 0);
edge_total = 0;
visit.assign(n, false);
for (int i = 0; i < n; i++)
{
cin >> arr[i];
edge_total += arr[i];
}
for (int i = 0; i < n-1; i++)
{
cin >> u >> v;
u--;
v--;
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs(0);
long long l = 0, r = edge_total;
long long result = -1;
while(l<=r)
{
long long mid = (l + r) >> 1;
visit.assign(n, false);
if( dfs_find(0, mid))
{
l=mid+1;
result = mid;
}
else
{
r=mid-1;
}
}
cout << result << "\n";
}
return 0;
}
c#
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
class Program
{
static BufferedStream sr = new BufferedStream(Console.OpenStandardInput());
static StreamWriter sw = new StreamWriter(new BufferedStream(Console.OpenStandardOutput()), Encoding.Default);
static int[] arr=new int [200001];
static List<List<int>> edge = new List<List<int>>(200001);
static long[] edge_v = new long[200001], edge_min = new long[200001], edge_len = new long[200001];
static long total = 0,k=0;
struct Pair:IComparable<Pair>
{
public long Key, Value;
public Pair(long Key,long Value)
{
this.Key = Key;
this.Value = Value;
}
public int CompareTo(Pair other)
{
return other.Key.CompareTo(this.Key);
}
}
static Pair[][] temps = new Pair[200001][];
static int[][] temp = new int[200001][];
static void Set_Edge(int idx,int parent)
{
edge_v[idx] = arr[idx];
var list = edge[idx];
if (temp[idx]==null||temp[idx].Length< list.Count) temp[idx] = new int[list.Count];
if (temps[idx]==null||temps[idx].Length <list.Count) temps[idx] = new Pair[list.Count];
int temp_i = 0;
for(int i=0;i< list.Count;i++)
{
int next=list[i];
if (next==parent) continue;
Set_Edge(next,idx);
edge_v[idx] += edge_v[next];
temp[idx][temp_i++] = next;
}
var t_list = temp[idx];
for (int i=0;i< temp_i;i++)
{
int next = t_list[i];
edge_min[next] = Math.Min(edge_v[next], edge_v[idx] - edge_v[next]);
}
}
static bool DFS(int idx,long mid,int parent)
{
int temps_i = 0;
var list = edge[idx];
for (int i=0;i< list.Count;i++)
{
int next=list[i];
if (next==parent) continue;
if (DFS(next, mid,idx)) return true;
if (mid <= edge_v[next])
{
temps[idx][temps_i++]=new Pair( edge_v[next], edge_len[next]);
}
if (mid<=edge_min[next])
{
edge_len[idx] = Math.Max(1 + edge_len[next], edge_len[idx] ) ;
}
if(edge_len[next] +1>=k&&total- edge_v[next] >=mid&& edge_v[next] >=mid)
{
return true;
}
}
if (temps_i >1) Array.Sort(temps[idx], 0, temps_i);
int ii = -1;
long l = -1, r = -1;
//아 무조건 l,r범위가 커지도록 하려면 작은쪽에서 커지도록 해야함;;
var t_list = temps[idx];
for (int j=0;j<temps_i;j++)
{
var i = t_list[j];
while (ii + 1 < temps_i && total - temps[idx][ii + 1].Key- i.Key >= mid)
{
ii++;
if (l <= temps[idx][ii].Value)
{
r = l;
l = temps[idx][ii].Value;
}
else if( r<= temps[idx][ii].Value) r = temps[idx][ii].Value;
}
if (ii ==-1) continue;
if (total - 2 * i.Key >= mid && l == i.Value)
{
if (i.Value + r + 2 >= k) return true;
}
else
if (l + i.Value+ 2 >= k) return true;
}
return false;
}
static int ReadInt()
{
int c = sr.ReadByte();
while (c <= 32) { if (c == -1) return -1; c = sr.ReadByte(); }
bool neg = false;
if (c == '-') { neg = true; c = sr.ReadByte(); }
int val = 0;
while (c > 32)
{
val = val * 10 + (c - '0');
c = sr.ReadByte();
}
return neg ? -val : val;
}
static void Solve()
{
int t = ReadInt();
for (int i = 0; i < 200001; i++)
{
edge.Add(new List<int>());
}
for (int i = 0; i < t; i++)
{
int n = ReadInt();
k = ReadInt();
total = 0;
for (int j = 0; j < n; j++)
{
arr[j] = ReadInt();
total += arr[j];
edge[j].Clear();
}
for (int j = 0; j < n - 1; j++)
{
int u = ReadInt() - 1;
int v = ReadInt() - 1;
edge[u].Add(v);
edge[v].Add(u);
}
Array.Clear(edge_v, 0, n);
Array.Clear(edge_min, 0, n);
Set_Edge(0, -1);
long l = 0, r = total, answer = -1;
while (l <= r)
{
Array.Clear(edge_len, 0, n);
long mid = (l + r) >> 1;
if (DFS(0, mid, -1))
{
l = mid + 1;
answer = mid;
}
else
{
r = mid - 1;
}
}
sw.WriteLine(answer);
}
sw.Flush();
}
static void Main(string[] args)
{
Thread thread = new Thread(Solve, 1024 * 1024 * 32);
thread.Start();
thread.Join();
}
}
// 스택 크기를 256MB로 지정한 쓰레드 생성
Thread thread = new Thread(Solve, 1024 1024 256);
thread.Start();
thread.Join();
아니 뭔데 아.. 최적화해도 안되네... 흠.. 일다 내일까지 더해보고.. 될 것 같은데 어디를 시간 줄여야할지 정확히 모르니.;;인덱싱도 복사해서 리스트 인덱싱도 줄이고 포문도 줄이고했는데 흠..