我逝去的贪心——贪心只能过样例 记录一次考试
考试T2。。。
呜呜呜
考试中:老刘!这个T2怎么这么水啊awa
然后,写代码的时候,老刘突然走过来,给我来一句:dalao,您AK了啊awa
我:???
然后我的T2就搞出来了,还过了大样例。
然后考完了,我16分。。。
T2的16分。。。
您AK了啊
我算是知道了老刘的毒奶是真的名不虚传啊。。。
嗯。
一看就是和树的重心有关的东西。
嗯
然后我就写了一个树的重心。
再算出以它为根的全部的子树的大小.(其实这个要找的是这个子树的重心,不是它的根)
再把子树全部放进一个优先队列里面 ,每次断边取出最大的子树,断掉上面的一条边。
到这里,我的贪心还是正确的。
但是,下一步
断掉子树中 最大子树 和子树根的边。
我想当然的以为这就是最大的,压根就没有想一下正确性。
然后,我就过了那个卡掉了一个贪心的大样例。
(没卡掉大概是因为我的贪心太sb了吧)
T2就快乐的拿到了16分。
嗯
正解
考虑我所说的树的重心。
发现我每一次都要求的树的重心,其实就是使最大子树最小的一个点
最大子树最小?
最大**最小?
二分答案。
那么是用什么的单调性呢?、
我们发现,设最大的子树为M,那么答案就是
找一下awa,就发现,其实如果我限制每个子树的最大的大小,那么删的边的数量就是在不严格单调递增的。
所以我们就用每个子树的大小去搞,如果一个子树是要大于我们的限制的话,那么就把它割一下,因为这个其实就是在最接近我设定的答案的地方割的,所以一定是最优的
直接用dfs暴力找,方法就是上面的方法。
这个很简单啊。。。
最后找出来的最大子树肯定是把k次割边的机会全部用完的,所以,就是以k为下界的。
就AC了
其实吧,这一题,就是对二分的一个理解。
有一说一,当看到他的答案与最小的最大子树相关的时候,就应该想到二分了。
但我只想到了树的重心。
唉,二分还是不够熟悉啊。
CODE
#include<bits/stdc++.h> #define ll long long #define mp(a,b) make_pair(a,b) using namespace std; struct edge { int next,to; }e[1000001]; int n,m,k; int head[1000001],tot,size[1000001],maxx=INT_MAX,maxxid,size2[1000001],father[1000001],ru[1000001],size3[1000001];int color[100001],cnt,p[1000001]; void init(int i,int j) { e[++tot].next=head[i]; e[tot].to=j; head[i]=tot; } int getf(int x) { if(father[x]==x)return x; else return father[x]=getf(father[x]); } inline ll read() { char c=getchar();ll a=0,b=1; for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1; for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48; return a*b; } //void Find_first(int x,int fa)//O(n) 我逝去的贪心 //{ // int maxSUb=0; // size[x]=1; // for(int i=head[x];i!=0;i=e[i].next) // { // int u=e[i].to; // if(u==fa)continue; // Find_first(u,x); // size[x]+=size[u]; // maxSUb=max(size[u],maxSUb); // } // maxSUb=max(maxSUb,n-size[x]); // if(maxSUb<maxx) // { // maxx=maxSUb; // maxxid=x; // } //} //void Find_size_second(int x,int fa)//O(n) //{ // size2[x]=1; // for(int i=head[x];i!=0;i=e[i].next) // { // int u=e[i].to; // if(u==fa)continue; // Find_size_second(u,x); // size2[x]+=size2[u]; // father[u]=x; // } //} void cheak(int x,int fa,int val) { int sum=1; for(int i=head[x];i!=0;i=e[i].next) { int u=e[i].to; if(u==fa)continue; cheak(u,x,val); sum+=size[u]; } if(sum>val) { int tmp=0; for(int i=head[x];i!=0;i=e[i].next) { int u=e[i].to; if(u==fa)continue; p[++tmp]=size[u]; } sort(p+1,p+1+tmp); for(int i=tmp;i>0;i--) { sum-=p[i]; cnt++; if(sum<=val)break; } } size[x]=sum; } int main() { freopen("penalty.in","r",stdin); freopen("penalty.out","w",stdout); n=read();k=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); init(x,y);init(y,x);ru[x]++;ru[y]++; } int root; for(int i=1;i<=n;i++) { if(ru[i]==1) { root=i; break; } } int l=1,r=n,Max=0; while(l<r-1) { int mid=l+r>>1; memset(size,0,sizeof(size)); cnt=0; // cout<<mid<<endl; cheak(root,0,mid); if(cnt>k)l=mid; else r=mid; } cheak(root,0,l); if(cnt<=k) Max=l; else Max=r; cout<<1ll*Max*(k+1)-1ll*n<<endl;; // for(int i=1;i<=n;i++)father[i]=i; // 纪念我逝去的贪心。。。 // Find_first(root,-1); // cout<<maxxid<<endl; // Find_size_second(maxxid,-1); // priority_queue<pair<int,int> > q; // for(int i=1;i<=n;i++)q.push(mp(size2[i],i));//O(nlogn) // for(int i=1;i<=k;i++)//O(k*n*log n) 我没了。。。 我wa了。。。 // { // pair<int,int> x=q.top(); // q.pop(); // int si=x.first; // int now=x.second; // int Ma=0,id=0; // for(int i=head[now];i!=0;i=e[i].next) // { // int u=e[i].to; // if(size2[u]>size2[now]||size[u]==size[maxxid]||father[u]!=now)continue; // if(Ma<=size2[u])Ma=size2[u],id=u; // } //// cout<<now<<' '<<id<<" This is the path which I delide"<<endl; // size2[now]-=size2[id]; // father[id]=id; // q.push(mp(size2[now],now)); // } // int maxxx=0,ans=0; // for(int i=1;i<=n;i++) // { // maxxx=max(maxxx,size2[i]); // } // for(int i=1;i<=n;i++)//O(nlogn) // { // if(color[getf(i)]==false) // { // color[getf(i)]=true; // ans+=abs(size2[getf(i)]-maxxx); // } // } // cout<<ans<<endl; return 0; }