• UOJ276 [清华集训2016] 汽水 【二分答案】【点分治】【树状数组】


    题目分析:

    这种乱七八糟的题目一看就是点分治,答案有单调性,所以还可以二分答案。

    我们每次二分的时候考虑答案会不会大于等于某个值,注意到系数$k$是无意义的,因为我们可以通过转化使得$k=0$。

    合并的过程相当于很多个向量,加起来后看斜率。

    注意单个向量也要判定。

    由于有了二分的答案$Ans$。判定变得简单多了,推一下。

    令$(A,C)$是从一个点到重心的一个向量,$(B,D)$是从另一个点到重心的向量。其中$A$和$B$是重心到该点的路径权值和,$C$和$D$是经过的边数。

    $-k leq frac{A+C}{B+D} leq k Rightarrow -k(B+D) leq A+C leq k(B+D)$.

    进一步的$A+kB geq -C-kD$且$A-kB leq kD-C$。虽然有四元,但是顺序相互关联,所以实际只有两元,排序后树状数组就可以解决啦。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 
      4 typedef long long ll;
      5 
      6 const int maxn = 50100;
      7 
      8 ll k,md; int flag = 0,num,n,rnum;
      9 vector <pair<int,ll> > g[maxn];
     10 int arr[maxn],sz[maxn],imp[maxn],cnt[maxn];
     11 struct node{ll A;int B,pla;}op[maxn];
     12 
     13 int cmp(node X,node Y){
     14     return -X.A-md*X.B < -Y.A-md*Y.B;
     15 }
     16 
     17 struct Fenwick{
     18     int C[maxn];
     19     void Add(int now){
     20     while(now <= rnum){C[now] ++; now += (now&-now);}
     21     }
     22     int query(int now){
     23     int ans = 0;
     24     while(now){ans += C[now]; now -= (now&-now);}
     25     return ans;
     26     }
     27 }T1;
     28 
     29 void read(){
     30     scanf("%d%lld",&n,&k);
     31     for(int i=1;i<n;i++){
     32     int x,y;long long v; scanf("%d%d%lld",&x,&y,&v); v -= k;
     33     g[x].push_back(make_pair(y,v)); g[y].push_back(make_pair(x,v));
     34     }
     35 }
     36 
     37 void dfs1(int now,int fa,int dp){
     38     sz[now] = 1;imp[now] = 0;
     39     for(auto it : g[now]){
     40     if((arr[it.first] && arr[it.first] < dp) || fa == it.first) continue;
     41     dfs1(it.first,now,dp); sz[now] += sz[it.first];
     42     }
     43 }
     44 
     45 int dfs2(int now,int fa,int dp,int ssz){
     46     int ans = 0;
     47     for(auto it : g[now]){
     48     if((arr[it.first] && arr[it.first] < dp) || fa == it.first) continue;
     49     int data = dfs2(it.first,now,dp,ssz);
     50     if(ans==0 || imp[ans] > imp[data])ans = data;
     51     imp[now] = max(sz[it.first],imp[now]);
     52     }
     53     imp[now] = max(imp[now],ssz-sz[now]);
     54     if(ans==0 || imp[ans] > imp[now]) ans = now;
     55     return ans;
     56 }
     57 
     58 void dfs3(int now,int fa,int dp,int A,int B){
     59     for(auto it : g[now]){
     60     if(it.first == fa || (arr[it.first] && arr[it.first] < dp)) continue;
     61     op[++num] = (node){A+it.second,B+1,it.first};
     62     dfs3(it.first,now,dp,A+it.second,B+1);
     63     }
     64 }
     65 
     66 long long lisan[maxn];
     67 void solve(int dr){
     68     rnum = num;
     69     for(int i=1;i<=num;i++){lisan[i] = -op[i].A+md*op[i].B;}
     70     sort(lisan+1,lisan+num+1);rnum = unique(lisan+1,lisan+num+1)-lisan-1;
     71     for(int i=1;i<=rnum;i++) T1.C[i]=0;
     72     for(int i=num,j=1;i>=1;i--){
     73     while(j <= num && (-op[j].A-md*op[j].B < op[i].A+md*op[i].B)){
     74         T1.Add(lower_bound(lisan+1,lisan+rnum+1,-op[j].A+md*op[j].B)-lisan);
     75         j++;
     76     }
     77     int ans=j-1-T1.query(upper_bound(lisan+1,lisan+rnum+1,op[i].A-md*op[i].B)-lisan-1);
     78     if(op[i].A-md*op[i].B < -op[i].A+md*op[i].B && j > i)ans--;
     79     if(dr == 1){
     80         if(ans - cnt[op[i].pla]){flag = 1;return;}
     81     }else{cnt[op[i].pla] = ans;}
     82     }
     83 }
     84 
     85 void divide(int now,int dp,int lst,long long AA){
     86     dfs1(now,0,dp); int heavy = dfs2(now,0,dp,sz[now]);arr[heavy] = dp;
     87     for(auto it : g[heavy]){
     88     if(arr[it.first]&&arr[it.first]<dp) continue;
     89     divide(it.first,dp+1,heavy,it.second);
     90     if(flag == 1) return;
     91     }
     92     num = 0; dfs3(heavy,0,dp,0,0); sort(op+1,op+num+1,cmp);
     93     solve(1); num = 0;
     94     if(lst) {
     95     num = 0;dfs3(now,0,dp,AA,1);
     96     sort(op+1,op+num+1,cmp);
     97     for(int i=1;i<=num;i++) cnt[op[i].pla] = 0;
     98     solve(0);
     99     }
    100 }
    101 
    102 void work(){
    103     long long l = 0,r = 1e13;
    104     while(l < r){
    105     md = (l+r)/2; flag = 0;
    106     memset(arr,0,sizeof(arr));
    107     divide(1,1,0,0);
    108     if(flag) r = md; else l = md+1;
    109     }
    110     printf("%lld",l-1);
    111 }
    112 
    113 int main(){
    114     read();
    115     work();
    116     return 0;
    117 }
  • 相关阅读:
    Reusable action with query database
    Oracle实现分组统计记录
    Oracle行列转换的几种实现方法
    junit私有方法测试
    Junit实现抽象类测试(二)
    C++的性能C#的产能?! .Net Native 系列《二》:.NET Native开发流程详解
    C++的性能C#的产能?! .Net Native 系列向导
    c++的性能, c#的产能?!鱼和熊掌可以兼得,.NET NATIVE初窥
    辞职敬礼
    WPF 心路历程
  • 原文地址:https://www.cnblogs.com/Menhera/p/9347630.html
Copyright © 2020-2023  润新知