zoukankan      html  css  js  c++  java
  • 并不对劲的uoj276. [清华集训2016]汽水

    想要很对劲的讲解,请点击这里

    题目大意

    有一棵(n)((nleq 50000))个节点的树,有边权
    求一条路径使该路径的边权平均值最接近给出的一个数(k)
    输出边权平均值下取整的整数部分

    题解

    有关路径的边权平均值可以二分来做
    假设已二分出边权平均值与(k)的差不超过(m),则应存在一条简单路径,路径上的(p)条边满足:

    [-mleqfrac{sum_{i=1}^{p}{w_i}}{p}-kleq m ]

    两边同乘(p),得:

    [-m*pleqsum_{i=1}^{p}{w_i-k}leq m*p ]

    也就是$$egin{cases}{sum_{i=1}^{p}{(w_i-k+m)}}geq 0\sum_{i=1}^{p}{(w_i-k-m)}leq 0end{cases}$$
    那么设(a_i=w_i-k+m,b_i=w_i-k-m),问题就变成了判断图中是否存在一条简单路径,满足(Sigma a_igeq 0)(Sigma b_ileq 0)
    这种在树上找满足某个条件的链的问题可以用点分治解决
    设某点(i)到当前区域的重心的简单路径上,(a_k)之和为(A_i)(b_k)之和为(B_i)
    则问题转化为找两点(i)(j)不属于当前区域的重心的同一子树,且(A_i+A_jgeq0)(B_i+B_jleq0)
    也可以看成是对于点(i),所有(A_jgeq-A_i)的点(j)中,(B_j)的最小值是否小于(-B_i)
    这个可以用平衡树或权值线段树来维护
    再算上二分、点分治的时间复杂度,总时间复杂度使(Theta(nspace log^3 n))的,看上去有点悬
    考虑加一些小优化,比如:
    1.预处理点分树,就不用二分时每次判断都求一遍重心;
    2.当找到一条合法的链后,立刻退出;
    3.没有必要把当前区域的重心的最后一个儿子的子树里的点的信息加入平衡树或权值线段树
    ...
    然而还是跑得很慢…

    #include<algorithm>
    #include<cmath>
    #include<complex>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<iomanip>
    #include<iostream>
    #include<map>
    #include<queue>
    #include<set>
    #include<stack>
    #include<vector>
    #define maxn 50010
    #define maxm (maxn<<1)
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define dwn(i,x,y) for(int i=(x);i>=(y);--i)
    #define ls son[u][0]
    #define rs son[u][1]
    #define eps 5e-3
    #define LD long double
    using namespace std;
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)&&ch!='-')ch=getchar();
    	if(ch=='-')ch=getchar(),f=-1;
    	while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    	return x*f;
    }
    const double inf=(1.0/0.0);
    double mx[maxn],key[maxn],key2[maxn],dep[maxn],dep2[maxn],w[maxm],K,L,R,mid,ans;
    int n,fa[maxn],son[maxn][2],rt,vis[maxn],ww[maxn],siz[maxn],nowsiz,mnsiz,wt,nxt[maxm],fir[maxn],v[maxm],cnt,nd,yes;
    int nt[maxn],vv[maxn],fs[maxn],cnt2,root;
    void ade(int u1,int v1,double w1){v[cnt]=v1,nxt[cnt]=fir[u1],w[cnt]=w1,fir[u1]=cnt++;}
    void ade2(int u1,int v1){vv[cnt2]=v1,nt[cnt2]=fs[u1],fs[u1]=cnt2++;}
    void getsiz(int u,int fa)
    {
    	siz[u]=1;int nowmx=0;
    	for(int k=fir[u];k!=-1;k=nxt[k])
    		if(v[k]!=fa&&!vis[v[k]])getsiz(v[k],u),siz[u]+=siz[v[k]],nowmx=max(nowmx,siz[v[k]]);
    	nowmx=max(nowmx,nowsiz-siz[u]);
    	if(nowmx<mnsiz)mnsiz=nowmx,wt=u;
    }
    void getwt(int u,int fa,int sumsiz)
    {
    	nowsiz=sumsiz,mnsiz=n+1,getsiz(u,0),vis[wt]=1;
    	int now=wt;
    	if(sumsiz==n)root=now;
    	else ade2(fa,now);
    	for(int k=fir[now];k!=-1;k=nxt[k])
    		if(!vis[v[k]])getwt(v[k],now,siz[v[k]]>siz[now]?sumsiz-siz[now]:siz[v[k]]);
    }
    void pu(int u)
    {
    	if(!u)return;
    	mx[u]=key2[u];
    	if(ls)mx[u]=min(mx[u],mx[ls]);
    	if(rs)mx[u]=min(mx[u],mx[rs]);
    	return;
    }
    int getso(int u){return son[fa[u]][0]!=u;}
    void rot(int u)
    {
    	register int fu=fa[u],ffu=fa[fu],l=getso(u),fl=getso(fu),r=l^1,rson=son[u][r];
    	if(ffu)son[ffu][fl]=u;son[u][r]=fu,son[fu][l]=rson;if(rson)fa[rson]=fu;fa[u]=ffu,fa[fu]=u;
    	pu(fu),pu(u);
    }
    void ins(double x,double y)
    {
    	register int u=rt,lst=0,to;
    	while(u&&key[u]!=x)lst=u,to=x>key[u]?1:0,u=son[u][to];	
    	if(!u)
    	{
    		u=++nd,ls=rs=0,key[u]=x,key2[u]=y,mx[u]=y,ww[u]=rand()*rand()%63427919,fa[u]=lst;
    		if(lst)son[lst][to]=u/*,upd2(lst)*/;
    		else rt=u; 
    		while(fa[u]&&ww[fa[u]]>ww[u])rot(u);
    		if(!fa[u])rt=u;
    	}
    	else {key2[u]=min(key2[u],y),mx[u]=min(mx[u],y);while(u)pu(u),u=fa[u];}return;
    }
    double ask(double x)
    {
    	int u=rt;double res=inf;
    	while(u)
    	{
    		if(x<=key[u]){res=min(res,min(key2[u],rs?mx[rs]:inf));u=ls;}
    		else u=rs;
    	}
    	return res;
    }
    void asky(int u,int fa)
    {
    	double res=ask(-dep[u]);
    	if(res!=inf){if(res+dep2[u]<=0){yes=1;return;}}
    	for(int k=fir[u];k!=-1;k=nxt[k])
    		if(!vis[v[k]]&&v[k]!=fa)
    		{
    			dep[v[k]]=dep[u]+w[k]+mid,dep2[v[k]]=dep2[u]+w[k]-mid,asky(v[k],u);
    			if(yes)return;
    		}
    }
    void addy(int u,int fa)
    {
    	ins(dep[u],dep2[u]);
    	for(int k=fir[u];k!=-1;k=nxt[k])
    		if(!vis[v[k]]&&v[k]!=fa)addy(v[k],u);
    }
    void reset(){rt=0;nd=0,mx[0]=inf;}
    void gety(int now)
    {
    	reset();
    	ins(0,0);
    	vis[now]=1;
    	int lst=0;
    	for(int k=fir[now];k!=-1;k=nxt[k])if(!vis[v[k]])lst=v[k];
    	for(int k=fir[now];k!=-1;k=nxt[k])
    	{
    		if(!vis[v[k]])
    		{
    			dep[v[k]]=w[k]+mid,dep2[v[k]]=w[k]-mid;
    			asky(v[k],now);
    			if(yes)return;
    			if(v[k]!=lst)addy(v[k],now);
    		}
    	}
    	for(int k=fs[now];k!=-1;k=nt[k])
    		{gety(vv[k]);if(yes)return;}
    }
    int main()
    {
    	srand(time(0));
    	nowsiz=n=read();scanf("%lf",&K);
    	memset(fir,-1,sizeof(fir));
    	memset(fs,-1,sizeof(fs));
    	rep(i,1,n-1){int x=read(),y=read();double z;scanf("%lf",&z);R=max(R,z-K<0.0?K-z:z-K),ade(x,y,z-K),ade(y,x,z-K);}
    	getwt(1,0,n);
    	ans=R;
    	while(R-L>=eps)
    	{
    		mid=(R+L)/2.0;yes=0;
    		rep(i,1,n)vis[i]=0;
    		gety(root);
    		if(yes)ans=min(ans,mid),R=mid;
    		else L=mid;
    	}
    	printf("%.0lf",floor(ans));
    	return 0;
    }
    
  • 相关阅读:
    PHP发红包程序限制红包的大小
    PHP函数
    HTTP Keep-Alive详解[转]
    PHP中PSR
    一级域名、二级域名和二级目录有什么区别?
    mysqli_query(“set nams utf8”)为FALSE
    Mysql中结果集(mysql_result)与Traversable
    Hbase系列(一)---- Hbase究竟是一个怎样的数据库(基础原理)
    java-快速失败与安全失败
    java-hashmap 工作原理
  • 原文地址:https://www.cnblogs.com/xzyf/p/10059681.html
Copyright © 2011-2022 走看看