本人NOIP的考试题……也是我NOIP2018唯一爆零的题目,正因如此,本人无缘NOIP2018一等奖。
少年醒醒,都9012年了,还搞什么啊,赶紧想正解……
本题的结构是一个树形的,因此我们可以用树形结构的相关知识解答。
本题的题目为“最小值最大”显然是二分答案的提示,因此我们二分答案最短的一条路径mid,在满足这个条件下判断是否能找出m条道路即可。
我们这样思考:在以i为根的子树内部,可以拼凑若干条道路(也可能没有),同时,我们选择一条不能拼出道路,且经过i点,而且最长的一条路径传给他的父亲,为他的父亲拼凑路径做贡献。
因此,我们对于每一个点都开一个multiset,对于每个结点,把所有传上来的值 val 放进一个 multiset ,其实这些值对答案有贡献就两种情况:
val≥k
vala+valb≥k
那么第一种情况可以不用放进 multiset,直接答案 +1 即可。第二种情况就可以对于每一个最小的元素,在 multiset 中找到第一个 ≥mid的数,拼成一条合法发路径,将两个数同时删去,最后把剩下最大的值传到那个结点的父亲
为什么这种解法是正确的?有没有可能对于有些情况直接传最大的数会使答案更大?
当然不会。这个数即使很大也只能对答案贡献加 1,在其没传上去的时候可以跟原来结点的值配对,也只能对答案贡献加 1
这就完成了我NOIP未了的心愿……
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <set> 6 using namespace std; 7 inline int read() { 8 int ret=0; 9 int op=1; 10 char c=getchar(); 11 while(c<'0'||c>'9') {if(c=='-') op=-1; c=getchar();} 12 while(c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar(); 13 return ret*op; 14 } 15 struct node { 16 int next,to,dis; 17 }a[50010<<1]; 18 int n,m,head[50010<<1],num,l=2147483647,r,sum; 19 multiset<int> s[50010]; 20 multiset<int>::iterator it; 21 inline void add(int from,int to,int dis) { 22 a[++num].next=head[from]; 23 a[num].to=to; 24 a[num].dis=dis; 25 head[from]=num; 26 } 27 inline int zhijing(int now,int fa) { 28 int sum1=0,sum2=0; 29 for(int i=head[now];i;i=a[i].next) 30 if(a[i].to!=fa) { 31 sum2=max(sum2,zhijing(a[i].to,now)+a[i].dis); 32 if(sum2>sum1) swap(sum2,sum1); 33 } 34 r=max(sum1+sum2,r); 35 return sum1; 36 } 37 int dfs(int now,int fa,int minn) { 38 int ret=0; 39 s[now].clear(); 40 for(int i=head[now];i;i=a[i].next) { 41 int v=a[i].to; 42 if(v==fa) continue ; 43 int val=dfs(v,now,minn)+a[i].dis; 44 if(val>=minn) sum++; 45 else s[now].insert(val); 46 } 47 while(!s[now].empty()) { 48 if(s[now].size()==1) return max(ret,*s[now].begin()); 49 it=s[now].lower_bound(minn-*s[now].begin()); 50 if(it==s[now].begin()&&s[now].count(*it)==1) it++; 51 if(it==s[now].end()) { 52 ret=max(ret,*s[now].begin()); 53 s[now].erase(s[now].find(*s[now].begin())); 54 } 55 else { 56 sum++; 57 s[now].erase(s[now].find(*it)); 58 s[now].erase(s[now].find(*s[now].begin())); 59 } 60 } 61 return ret; 62 } 63 inline bool check(int val) { 64 sum=0; 65 dfs(1,0,val); 66 return sum>=m; 67 } 68 int main() { 69 n=read(); m=read(); 70 for(int i=1,x,y,z;i<n;i++) { 71 x=read(); y=read(); z=read(); 72 add(x,y,z); 73 add(y,x,z); 74 l=min(l,z); 75 } 76 zhijing(1,0); 77 int ans=0; 78 while(l<=r) { 79 int mid=(l+r)>>1; 80 if(check(mid)) { 81 ans=mid; 82 l=mid+1; 83 } 84 else r=mid-1; 85 } 86 printf("%d\n",ans); 87 return 0; 88 }