1. 程式人生 > >UOJ276 [清華集訓2016] 汽水 【二分答案】【點分治】【樹狀數組】

UOJ276 [清華集訓2016] 汽水 【二分答案】【點分治】【樹狀數組】

答案 很多 right turn lower pair first upper sizeof

題目分析:

這種亂七八糟的題目一看就是點分治,答案有單調性,所以還可以二分答案。

我們每次二分的時候考慮答案會不會大於等於某個值,註意到系數$k$是無意義的,因為我們可以通過轉化使得$k=0$。

合並的過程相當於很多個向量,加起來後看斜率。

註意單個向量也要判定。

由於有了二分的答案$Ans$。判定變得簡單多了,推一下。

$-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 }

UOJ276 [清華集訓2016] 汽水 【二分答案】【點分治】【樹狀數組】