[uoj276][清華集訓2016]汽水——分數規劃+點分治
阿新 • • 發佈:2019-02-13
etc oid 點分治 def auth || ems 既然 求平均值
\[
\sum_{i=1}^{len}w_i-k+x \geq 0\\sum_{i=1}^{len}w_i-k-x \leq 0
\]
由於這裏是樹上的路徑問題,考慮用點分治來解決,考慮以某一個分治重心為根的子樹內所有點,按照他們到根的邊權和從小到大排序,我們考慮每一個點在滿足第一維的情況下最小化第二維的值,這樣只需要two point 掃一下就好了。
題目大意:
給定一顆帶邊權的樹,求一條路徑使得這條路徑上的邊權的平均值最接近一個給定的值。
思路:
既然是求平均值,那麽自然而然就想到了分數規劃了, 即最小化\(|\frac{\sum_{i=1}^{{len}}w_i}{len}-k|\)。
然後二分答案\(x\),考慮是否存在比\(x\)更優的答案:\(|\frac{\sum_{i=1}^{{len}}w_i}{len}-k|\leq x\),帶有絕對值的好像不太好處理,於是將絕對值拆開:\(-x\leq \frac{\sum_{i=1}^{{len}}w_i}{len}-k \leq x\),一般的分數規劃都是求一個式子的最值,而這裏不難發現需要有兩個式子的值同時滿足:
由於這裏是樹上的路徑問題,考慮用點分治來解決,考慮以某一個分治重心為根的子樹內所有點,按照他們到根的邊權和從小到大排序,我們考慮每一個點在滿足第一維的情況下最小化第二維的值,這樣只需要two point 掃一下就好了。
/*======================================= * Author : ylsoi * Time : 2019.2.13 * Problem : uoj276 * E-mail : [email protected] * ====================================*/ #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i) #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i) #define debug(x) cout<<#x<<"="<<x<<" " #define fi first #define se second #define mk make_pair #define pb push_back typedef long long ll; using namespace std; void File(){ freopen("uoj276.in","r",stdin); freopen("uoj276.out","w",stdout); } template<typename T>void read(T &_){ _=0; T f=1; char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0'); _*=f; } const int maxn=5e4+10; const int inf=0x3f3f3f3f; const ll INF=1e18; int n; ll k; int beg[maxn],to[maxn<<1],las[maxn<<1],cnte=1; long double w[maxn<<1],c1[maxn<<1],c2[maxn<<1]; void add(int u,int v,long double ww){ las[++cnte]=beg[u],beg[u]=cnte,to[cnte]=v,w[cnte]=ww; las[++cnte]=beg[v],beg[v]=cnte,to[cnte]=u,w[cnte]=ww; } int sz[maxn],min_sz,tot_sz,rt; bool vis[maxn]; void get_rt(int u,int fh){ int max_sz=0; sz[u]=1; for(int i=beg[u];i;i=las[i]){ int v=to[i]; if(v==fh || vis[v])continue; get_rt(v,u); max_sz=max(max_sz,sz[v]); sz[u]+=sz[v]; } max_sz=max(max_sz,tot_sz-sz[u]); if(max_sz<min_sz){ min_sz=max_sz; rt=u; } } struct node{ long double d1,d2; int from; bool operator < (const node & tt) const { return d1<tt.d1; } }a[maxn]; int cnt_dis; void get_dis(int u,int fh,long double d1,long double d2,int from){ a[++cnt_dis]=(node){d1,d2,from}; for(int i=beg[u];i;i=las[i]){ int v=to[i]; if(v==fh || vis[v])continue; get_dis(v,u,d1+c1[i],d2+c2[i],from); } } void chkmin(int &mn,int &nx,int p){ if(a[p].d2<a[mn].d2){ if(a[p].from!=a[mn].from)nx=mn; mn=p; } else if(a[p].d2<a[nx].d2 && a[p].from!=a[mn].from)nx=p; } bool solve(int u){ a[cnt_dis=1]=(node){0,0,u}; for(int i=beg[u];i;i=las[i]){ int v=to[i]; if(vis[v])continue; get_dis(v,u,c1[i],c2[i],v); } sort(a+1,a+cnt_dis+1); int mn=0,nx=0,p=1,q=1; //[p,q-1] is ok a[0].d2=INF; while(a[q].d1<0)++q,++p; while(q<=cnt_dis){ while(p>1 && a[p-1].d1+a[q].d1>=0) chkmin(mn,nx,--p); if(a[mn].from!=a[q].from && a[mn].d2+a[q].d2<=0)return true; if(a[mn].from==a[q].from && a[nx].d2+a[q].d2<=0)return true; chkmin(mn,nx,q++); } return false; } bool divide(int u){ vis[u]=1,get_rt(u,0); if(solve(u))return true; for(int i=beg[u];i;i=las[i]){ int v=to[i]; if(vis[v])continue; tot_sz=sz[v],min_sz=inf; get_rt(v,0); if(divide(rt))return true; } return false; } bool jud(long double x){ REP(i,2,cnte)c1[i]=w[i]+x,c2[i]=w[i]-x; memset(vis,0,sizeof(vis)); tot_sz=n,min_sz=inf; get_rt(1,0); return divide(rt); } int main(){ File(); read(n),read(k); int u,v; ll ww; REP(i,1,n-1){ read(u),read(v),read(ww); add(u,v,(ww-k)*1.0); } long double l=0,r=1e13,mid; while(r-l>1e-3){ mid=(l+r)/2; if(jud(mid))r=mid; else l=mid; //printf("%.10Lf %.10Lf\n",l,r); } printf("%lld\n",(ll)(r)); return 0; }
[uoj276][清華集訓2016]汽水——分數規劃+點分治