[UOJ276] [2016] Tsinghua training soda (Score + dotted planning rule)

Click here to see the problem surface

Generally meaning of problems: to give you a tree, the tree requires you to choose a route, so that the upper path and the right average value \ (K \) the absolute value of the smallest difference. To obtain this minimum.

Fractional Programming

See the mean, first of all we should think fraction of planning it.

We dichotomous answer \ (the X-\) , set up to select the \ (m \) edges, each right Collage is \ (a_i \) .

The answer \ (x \) legitimate need to be met:

\ [| \ Frac {\ sum_ {i = 1}} 'ma_i mk | \ le x \]

Demolition absolute value:

\ [- x \ the \ frac {\ sum_ {at} = 1} ^ ma_i mk \ X \]

Each formula at the same time multiplied by \ (m \) , go denominator:

\ [- mx \ the \ sum_ {at = 1} ^ ma_i-mk \ The mx \]

These can be divided into two parts to consider the whole equation.

For the left part:

\[\sum_{i=1}^ma_i-mk\ge -mx\]

\[\sum_{i=1}^ma_i-mk+mx\ge0\]

\[\sum_{i=1}^m(a_i-k+x)\ge0\]

For the right half:

\ [\ Sum_ {i = 1} ^ ma_i-mk \ le mx \]

\ [\ Sum_ {i = 1} ^ ma_i-mk-mx \ le0 \]

\[\sum_{i=1}^m(a_i-k-x)\le0\]

In other words, we need only determine whether there is a path, both of the following conditions:

\[\sum_{i=1}^m(a_i-k+x)\ge0\]

\[\sum_{i=1}^m(a_i-k-x)\le0\]

Not difficult to find, if set \ (a_i-v_i = k \) , then the equation becomes:

\[\sum_{i=1}^m(v_i+x)\ge0\]

\[\sum_{i=1}^m(v_i-x)\le0\]

In fact, this is the right of the reading side will be subtracted \ (k \) , will be able to simplify many of the equation.

Dotted rule

For tree routing problem, most likely to think that dotted rule of.

For each partition Center \ (RT \) , we first traverse its sub-tree, for its sub-tree for each point \ (i \) , stored under three information:

  1. \ (I \) in \ (RT \) sub-tree which sub-node.
  2. \ (I \) to \ (RT \) on the path \ (\ SUM (X + V) \) .
  3. \ (I \) to \ (RT \) on the path \ (\ SUM (VX) \) .

To meet the requirements, it is necessary the presence of two path or a path not in the same subtree of child nodes of \ (\ sum (v + x ) \ ge0 \) and \ (\ SUM (VX) \ as le0 \) .

In this regard, we only need to Node Information \ (\ sum (v + x ) \) from small to large, enumerated a path with a double pointer idea to maintain the path of another optional section.

Optional recording interval \ (\ sum (vx) \ ) is the minimum value and the minimum value is not within the same sub-sub-tree nodes other minimum value , then can determine whether there is a path for which another path its matching can meet the conditions.

Code

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50000
#define LL long long
#define DB long double
#define INF 1e18
#define eps 1e-4
#define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,ee,lnk[N+5];struct edge {int to,nxt;LL v;}e[N<<1];
class FastIO
{
    private:
        #define FS 100000
        #define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
        #define pc(c) (C==E&&(clear(),0),*C++=c)
        #define tn (x<<3)+(x<<1)
        #define D isdigit(c=tc())
        char c,*A,*B,FI[FS];
    public:
        I FastIO() {A=B=FI;}
        Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
        #undef D
}F;
class DotDivideSolver//点分治
{
    private:
        DB p;int rt,sum,cnt,vis[N+5],Sz[N+5],Mx[N+5];
        struct data//存储一个节点的信息
        {
            int i;DB x,y;I data(CI p=0,Con DB& a=0,Con DB& b=0):i(p),x(a),y(b){}
            I bool operator < (Con data& o) Con {return x<o.x;}//用于排序
        }s[N+5],Mn,Sn;
        I void GetRt(CI x,CI lst=0)//找重心
        {
            Sz[x]=1,Mx[x]=0;for(RI i=lnk[x];i;i=e[i].nxt) !vis[e[i].to]&&
                e[i].to^lst&&(GetRt(e[i].to,x),Sz[x]+=Sz[e[i].to],Gmax(Mx[x],Sz[e[i].to]));
            Gmax(Mx[x],sum-Sz[x]),Mx[x]<Mx[rt]&&(rt=x);
        }
        I void Travel(CI x,CI lst,CI ty,Con DB& a,Con DB& b)//遍历某一子节点的子树
        {
            s[++cnt]=data(ty,a,b);for(RI i=lnk[x];i;i=e[i].nxt)//存储下节点信息
                !vis[e[i].to]&&e[i].to^lst&&(Travel(e[i].to,x,ty,a+e[i].v+p,b+e[i].v-p),0);
        }
        I bool Work(CI x,CI lst=0)
        {
            RI i,j;for(vis[x]=1,cnt=0,i=lnk[x];i;i=e[i].nxt)//遍历子树
                !vis[e[i].to]&&e[i].to^lst&&(Travel(e[i].to,x,e[i].to,e[i].v+p,e[i].v-p),0);
            for(Mn=Sn=data(0,INF,INF),sort(s+1,s+cnt+1),i=1,j=cnt;i<=cnt;++i)//排序
            {
                if(s[i].x>=0&&s[i].y<=0) return true;//如果这单条路径符合条件,返回true
                W(j&&s[i].x+s[j].x>=0)//双指针
                    s[j].y<Mn.y?(Mn.i^s[j].i&&(Sn=Mn,0),Mn=s[j],0)//与最小值相比较
                    :Mn.i^s[j].i&&s[j].y<Sn.y&&(Sn=s[j],0),--j;//与另一最小值相比较
                if(s[i].i^Mn.i?(s[i].y+Mn.y<0):(s[i].y+Sn.y<0)) return true;//判断是否合法
            }
            for(i=lnk[x];i;i=e[i].nxt) if(!vis[e[i].to])//递归处理子树
                if(rt=0,sum=Sz[e[i].to],GetRt(e[i].to,x),Work(rt,x)) return true;
            return false;
        }
    public:
        I void Clear() {memset(vis,0,sizeof(vis));}//清空
        I bool Solve(Con DB& x) {return p=x,Clear(),Mx[rt=0]=sum=n,GetRt(1),Work(rt);}//点分验证二分的答案
}D;
int main()
{
    RI i,x,y;LL z,t;for(F.read(n),F.read(t),i=1;i^n;++i)
        F.read(x),F.read(y),F.read(z),add(x,y,z-t),add(y,x,z-t);//建边,将边权减k
    DB l=0,r=INF,mid;W(r-l>eps) D.Solve(mid=(l+r)/2)?r=mid:l=mid;//二分答案
    return printf("%.0lf",(double)floor(r)),0;//输出答案,注意精度
}

Guess you like

Origin www.cnblogs.com/chenxiaoran666/p/UOJ276.html