NOIP2018 Day1 T3 赛道修建 - 二分套二分 - dp - 贪心

场上心路历程:
首先这显然好像是个dp。卧槽这个题能多项式?
考虑了一会列了个部分分表,发现我会做m=1求直径,会做链,还以为会做星星。
嗯确定了一定是dp,一定要二分答案。
然后写完暴力开始写链的时候突然想,诶星星怎么做来着???
仔细冷静了一下发现星星想得有问题。
开始慌,写了一半的链弃。
冷静了一下想起了以前cf做的一个题,当时读错题了,在读错题的基础上有人跟我说了个错误做法,当时大意是dp的第二维状态可以贪心,不过对那个错题是错结论。
猜测了二分答案后可以类似做,想了想没啥反例。
然后想了一堆不知道什么垃圾之后得到了一个初步想法,即现在有一些链,你要尽可能多的两两配对使得配对的长度和大于等于二分的值,做法是已经大于等于二分的值的链不管它,然后从大到小每个都选择尽可能小的配对。
这样没有过大样例及对拍,对拍了小数据发现这样并不能保证最后留下来的是最大的,结合数据范围意识到还需要一个二分,才过了大样例和对拍。
做完了。

#include
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define gc getchar()
using namespace std;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=50010;
struct edges{
    int to,pre,wgt;
}e[N<<1];int h[N],etop,a[N],b[N],c[N];
inline int add_edge(int u,int v,int w)
{ return e[++etop].to=v,e[etop].pre=h[u],e[etop].wgt=w,h[u]=etop; }
namespace subtask_std{
    int sv[N],res[N],vis[N];
    inline int check(int cnt,int bp,int v)
    {
        rep(i,1,cnt) vis[i]=0;if(bp) vis[bp]=1;int mxans=0;
        for(int i=cnt,j=1;i;i--)
        {
            if(vis[i]) continue;while(j<i&&(vis[j]||sv[j]+sv[i]<v)) j++;
            if(j<i&&!vis[j]&&sv[j]+sv[i]>=v) vis[i]=vis[j]=1,mxans++,j++;
        }
        return mxans;
    }
    int dfs(int x,int fa,int v)
    {
        int cnt=0,ans=0;
        for(int i=h[x],y;i;i=e[i].pre)
            if((y=e[i].to)^fa) ans+=dfs(y,x,v);
        for(int i=h[x],y;i;i=e[i].pre)
            if((y=e[i].to)^fa) sv[++cnt]=res[y]+e[i].wgt;
        res[x]=0;if(!cnt) return ans;
        sort(sv+1,sv+cnt+1);
        while(cnt&&sv[cnt]>=v) ans++,cnt--;
        int mxans=check(cnt,0,v),L=1,R=cnt,mid=(L+R)>>1;
        while(L<=R)
        {
            if(check(cnt,mid,v)==mxans) L=mid+1;
            else R=mid-1;mid=(L+R)>>1;
        }
        return res[x]=sv[R],ans+=mxans;
    }
    inline int acceptable_solution(int n,int m)
    {
        memset(h,0,sizeof(int)*(n+1)),etop=0;
        rep(i,1,n-1) add_edge(a[i],b[i],c[i]),add_edge(b[i],a[i],c[i]);
        int L=1,R=0;rep(i,1,n-1) R+=c[i];
        for(int mid=(L+R)>>1;L<=R;mid=(L+R)>>1)
            if(dfs(1,0,mid)>=m) L=mid+1;
            else R=mid-1;
        return !printf("%d\n",R);
    }
}
int main()
{
    int n=inn(),m=inn();
    rep(i,1,n-1) a[i]=inn(),b[i]=inn(),c[i]=inn();
    return subtask_std::acceptable_solution(n,m);
}

你可能感兴趣的:(二分,DP动态规划,贪心,树形dp)