zoukankan      html  css  js  c++  java
  • P6670 [清华集训2016] 汽水

    题目

    P6670 [清华集训2016] 汽水

    给一棵树,边有边权,要求找到一条路径使得其平均值和 (k) 最接近。

    分析

    首先树上路径容易想到点分治。

    然后发现这可以套一个 0/1 分数规划,于是我们可以把所有的边权减掉 (k),再二分 (mid)

    现在的问题就是判断了。

    我们发现答案具有单调性,于是我们可以排序过后双指针维护路径。

    题解

    代码

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>
    inline void read(T &x){
    	x=0;bool f=false;char ch=getchar();
    	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
    	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	x=f?-x:x;
    	return ;
    } 
    template <typename T>
    inline void write(T x){
    	if(x<0) putchar('-'),x=-x;
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    	return ;
    }
    #define ll long long
    const int N=2e5+5;
    const ll INF=1e18+7;
    ll Ans,k;
    int n;
    int head[N],nex[N],to[N],siz[N],idx;
    ll val[N];
    bool vis[N];
    int Size,Root,FMax;
    inline void add(int u,int v,ll w){
    	nex[++idx]=head[u];
    	to[idx]=v;
    	val[idx]=w;
    	head[u]=idx;
    	return ;
    }
    void GetRoot(int x,int fa){
    	siz[x]=1;int Max=0;
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(vis[y]||y==fa) continue;
    		GetRoot(y,x);siz[x]+=siz[y];
    		Max=max(Max,siz[y]);
    	}
    	Max=max(Max,Size-siz[x]);
    	if(Max<=FMax) FMax=Max,Root=x;
    	return ;
    }
    struct Edge{
    	ll sum;int num,bel;
    	Edge(ll sum=0,int num=0,int bel=0):sum(sum),num(num),bel(bel){}
    	inline bool operator < (const Edge &B)const{return sum<B.sum;}
    }p[N],M1,M2;
    int Cnt;
    void GetPath(int x,int fa,ll W,int D,int bel){
    	p[++Cnt]=Edge(W,D,bel);
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(y==fa||vis[y]) continue;
    		GetPath(y,x,W+val[i],D+1,bel);
    	}
    	return ;
    }
    inline void Modify(Edge now,ll mid){
    	if(now.sum<M1.sum){
    		if(now.bel==M1.bel) return M1=now,void();
    		return M2=M1,M1=now,void();
    	}
    	if(now.sum<M2.sum&& now.bel^M1.bel) M2=now;
    	return ;
    }
    inline bool Check1(ll mid){
    	int j=Cnt;
    	M1=M2=Edge(INF,-1,-1);
    	for(int i=1;i<=Cnt;i++){
    		for(;j&&p[i].sum+p[j].sum>=0;j--) Modify(Edge(p[j].sum-p[j].num*mid,-1,p[j].bel),mid);
    		if(p[i].num*mid-p[i].sum>(p[i].bel==M1.bel?M2.sum:M1.sum)) return true;
    	}
    	return false;
    }
    inline bool Check2(ll mid){
    	int j=1;
    	M1=M2=Edge(INF,-1,-1);
    	for(int i=Cnt;i>=1;i--){
    		for(;j<=Cnt&&p[i].sum+p[j].sum<0;j++) Modify(Edge(-p[j].sum-p[j].num*mid,-1,p[j].bel),mid);
    		if(p[i].num*mid+p[i].sum>(p[i].bel==M1.bel?M2.sum:M1.sum)) return true;
    	}
    	return false;
    }
    void DFS(int x){
    	vis[x]=true;Cnt=0;
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(vis[y]) continue;
    		GetPath(y,x,val[i],1,y);
    	}
    	sort(p+1,p+Cnt+1);
    	ll l=1,r=Ans;
    	while(l<=r){
    		ll mid=l+r>>1;
    		if(Check1(mid)||Check2(mid)) Ans=mid-1,r=mid-1; 
    		else l=mid+1;
    	}
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(vis[y]) continue;
    		Root=y,Size=FMax=siz[y],GetRoot(y,x);DFS(Root);
     	}
     	return ;
    }
    int main(){
    	read(n),read(k);Ans=INF;
    	for(int i=1;i<n;i++){
    		int u,v;ll w;
    		read(u),read(v),read(w);
    		add(u,v,w-k),add(v,u,w-k);
    		Ans=min(Ans,1ll*abs(w-k));
    	}
    	Root=1,FMax=Size=n;GetRoot(1,0),DFS(Root);
    	write(Ans);
    	return 0;
    }
    
  • 相关阅读:
    RHEL5.8使用yum安装xclock
    Linux下磁盘分区、挂载、卸载操作记录
    CentOS6.5磁盘分区和挂载操作记录
    CentOS环境下下调整home和根分区大小
    PowerDesigner连接Oracle数据库(32位)反向生成物理数据模型
    Create-React-App脚手架使用方法
    react 简书开发笔记
    本地连接服务器的mongodb
    使用mongoose连接mongodb(转载文章)
    将koa+vue部署到服务器
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14737364.html
Copyright © 2011-2022 走看看