• UOJ #276. 【清华集训2016】汽水


    TMD写了一篇博客竟然还不够清醒,那就再写一篇睡觉去了

    首先一看题目就会发现这可以先套上个分数规划,即我们现在要最小化(|frac{sum_{i=1}^{len} w_i}{len}-k|)

    考虑二分答案(x),顺便拆掉绝对值,即(-xlefrac{sum_{i=1}^{len} w_i}{len}-kle x),发现其实只要同时满足:

    [sum_{i=1}^{len} w_i-k+x ge 0 ]

    [sum_{i=1}^{len} w_i-k-x le 0 ]

    所以(k)不用管直接减掉即可,考虑就是找一条路径同时满足上述两个条件

    然后又考虑到这是树上问题,我们考虑点分治,如果单看第一个条件很简单,只要把分治中心到所有点的边权和搞出来,找两条(或一条)长度大于(0)的即可

    那么还有第二个要求怎么办,不难发现因为只用取两条链,因此我们可以在满足第一个的情况下最小化第二个的值,这个可以排序后two points扫一下解决

    注意一下两条链都在同一子树内的情况要判一下,总体复杂度(O(nlog^2 n))

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #define int long long
    #define RI register int
    #define CI const int&
    using namespace std;
    typedef long double DB;
    const int N=100005,INF=1e18;
    const DB EPS=1e-3;
    struct edge
    {
    	int to,nxt; DB v1,v2;
    }e[N<<1]; int n,head[N],cnt,k,x,y,z;
    inline void addedge(CI x,CI y,CI z)
    {
    	e[++cnt]=(edge){y,head[x],(DB)z-k,(DB)z-k}; head[x]=cnt;
    	e[++cnt]=(edge){x,head[y],(DB)z-k,(DB)z-k}; head[y]=cnt;
    }
    class Point_Division_Solver
    {
    	private:
    		struct data
    		{
    			int id; DB d1,d2;
    			friend inline bool operator < (const data& A,const data& B)
    			{
    				return A.d1<B.d1;
    			}
    		}d[N]; int size[N],mx[N],ots,tot,rt; bool vis[N];
    		#define to e[i].to
    		inline void getrt(CI now,CI fa=0)
    		{
    			size[now]=1; mx[now]=0; for (RI i=head[now];i;i=e[i].nxt) if (to!=fa&&!vis[to])
    			getrt(to,now),size[now]+=size[to],mx[now]=max(mx[now],size[to]);
    			if ((mx[now]=max(mx[now],ots-size[now]))<mx[rt]) rt=now;
    		}
    		inline void travel(CI now,CI fr,CI fa,const DB& dis1,const DB& dis2)
    		{
    			d[++tot]=(data){fr,dis1,dis2}; for (RI i=head[now];i;i=e[i].nxt)
    			if (to!=fa&&!vis[to]) travel(to,fr,now,dis1+e[i].v1,dis2+e[i].v2);
    		}
    		inline void insert(int& mi,int& smi,CI p)
    		{
    			if (d[p].d2<d[mi].d2) { if (d[p].id!=d[mi].id) smi=mi; mi=p; }
    			else if (d[p].d2<d[smi].d2&&d[p].id!=d[mi].id) smi=p;
    		}
    		inline bool judge(CI rt)
    		{
    			RI i,pos,mi,smi; for (d[tot=1]=(data){rt,0,0},i=head[rt];i;i=e[i].nxt)
    			if (!vis[to]) travel(to,to,rt,e[i].v1,e[i].v2); 
    			sort(d+1,d+tot+1); d[0].d2=INF;	for (i=pos=1;d[i].d1<0;++i,++pos);
    			for (mi=smi=0;i<=tot;++i)
    			{
    				while (pos>1&&d[pos-1].d1+d[i].d1>=0) insert(mi,smi,--pos);
    				if (d[mi].id!=d[i].id&&d[mi].d2+d[i].d2<=0) return 1;
    				if (d[mi].id==d[i].id&&d[smi].d2+d[i].d2<=0) return 1;
    				insert(mi,smi,i);
    			}
    			return 0;
    		}
    		inline bool solve(CI now)
    		{
    			vis[now]=1; if (judge(now)) return 1;
    			for (RI i=head[now];i;i=e[i].nxt) if (!vis[to])
    			{
    				mx[rt=0]=INF; ots=size[to]; getrt(to);
    				if (solve(rt)) return 1;
    			}
    			return 0;
    		}
    		#undef to
    	public:
    		inline bool check(const DB& x)
    		{
    			//printf("%lld : ",(int)x); 
    			RI i; for (i=1;i<=n;++i) vis[i]=0;
    			for (i=1;i<=cnt;++i) e[i].v1+=x,e[i].v2-=x;
    			mx[rt=0]=INF; ots=n; getrt(1); bool flag=solve(rt);
    			for (i=1;i<=cnt;++i) e[i].v1-=x,e[i].v2+=x; return flag;
    		}
    }PD;
    signed main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i; for (scanf("%lld%lld",&n,&k),i=1;i<n;++i)
    	scanf("%lld%lld%lld",&x,&y,&z),addedge(x,y,z);
    	DB l=0,r=1e13,mid; while (r-l>EPS)
    	if (PD.check(mid=(l+r)/2.0)) r=mid; else l=mid;
    	return printf("%lld",(int)(r)),0;
    }
    
  • 相关阅读:
    PAT 2016 数据的交换输出
    HDU 2020 绝对值排序
    HDU 2013 蟠桃记
    HDU 2005 第几天?
    HDU 2004 成绩转换
    系统时钟初始化
    array_map 去除数组参数里面左右两端空格
    建立自己的异常类方式
    laravel withCount 统计关联数量
    laravel门面和服务提供者使用
  • 原文地址:https://www.cnblogs.com/cjjsb/p/11616519.html
Copyright © 2020-2023  润新知