[WC2010]重建计划
题目大意:
给定一棵带权树,试找出长度在L和U之间的路径,使得权值和与路径长度比最大,求出这个权值。
思路:
“权值和与路径长度比最大”显然是一个经典的分数规划问题,考虑使用二分答案,
设每次二分出来的答案是m,将树上的每一条边减去m,这样问题就转化成了寻找一条边权和大于等于0的路径。
每次二分检验时,可以使用点分,找出经过当前重心的满足条件的路径。
我们可以先将子树内各个结点到重心的路径的边权和球出来,如果每次枚举每个点对,那么复杂度是O(n^2)的,显然会TLE。
经过观察,我们可以发现,每条路径仅与其边权和及路径长度有关,因此我们可以考虑对于每个相同的路径长度,记录其中最大的边权和,
使用两个数组max和tmax,max记录的是当前重心的前几个子树最大边权和,于每个tmax[i],它对应的l,u区间是可以单向滑动的,因此我们可以使用单调队列在线性时间内维护一棵子树。
因为树是静态的,因此每次找到的重心都不会变,如果每次二分以后都要找一边重心,显然是很不划算的,因此我们可以先预处理出每一次找到的中心,另外,对于大小小于l的子树,肯定不存在合法的路径,因此可以直接忽略。
这道题似乎特别卡常,而且容易栈溢出(即使没有死递归)。最后还是手动开大栈才过的。
1 #include<deque> 2 #include<cstdio> 3 #include<cctype> 4 #include<vector> 5 #include<cstring> 6 #include<algorithm> 7 inline int getint() { 8 char ch; 9 while(!isdigit(ch=getchar())); 10 int x=ch^'0'; 11 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 12 return x; 13 } 14 const int inf=0x7fffffff,_inf=-0x80000000; 15 const double eps=1e-4; 16 const int V=100001; 17 struct Edge { 18 int to,w; 19 }; 20 std::vector<Edge> e[V]; 21 inline void add_edge(const int u,const int v,const int w) { 22 e[u].push_back((Edge){v,w}); 23 } 24 bool is_centroid[V]={0}; 25 int centroid[V]={0},size[V]; 26 int now_centroid,tree_size,min_size; 27 void get_size(const int x,const int par) { 28 size[x]=1; 29 int max=0; 30 for(unsigned i=0;i<e[x].size();i++) { 31 int &y=e[x][i].to; 32 if(is_centroid[y]||y==par) continue; 33 get_size(y,x); 34 size[x]+=size[y]; 35 max=std::max(max,size[y]); 36 } 37 max=std::max(max,tree_size-size[x]); 38 if(max<min_size) { 39 min_size=max; 40 centroid[centroid[0]]=x; 41 } 42 } 43 void get_size2(const int x,const int par) { 44 size[x]=1; 45 for(unsigned i=0;i<e[x].size();i++) { 46 int &y=e[x][i].to; 47 if(is_centroid[y]||y==par) continue; 48 get_size2(y,x); 49 size[x]+=size[y]; 50 } 51 } 52 int l,u; 53 inline bool cmp(const Edge a,const Edge b) { 54 return size[a.to]<size[b.to]; 55 } 56 void get_centroid(const int x,const int sz) { 57 if(sz<l) return; 58 tree_size=sz; 59 min_size=inf; 60 centroid[0]++; 61 get_size(x,0); 62 is_centroid[centroid[centroid[0]]]=true; 63 int &now_centroid=centroid[centroid[0]]; 64 get_size2(now_centroid,0); 65 std::sort(e[now_centroid].begin(),e[now_centroid].end(),cmp); 66 for(unsigned i=0;i<e[now_centroid].size();i++) { 67 int &y=e[now_centroid][i].to; 68 if(is_centroid[y]) continue; 69 get_centroid(y,size[y]); 70 } 71 } 72 int max_depth,tmax_depth; 73 double max[V],tmax[V]; 74 double m; 75 void get_max(const int x,const int par,const int dep,const double dis) { 76 if(dep>tmax_depth) { 77 tmax_depth++; 78 tmax[dep]=dis-m*dep; 79 } 80 else { 81 tmax[dep]=std::max(tmax[dep],dis-m*dep); 82 } 83 for(unsigned i=0;i<e[x].size();i++) { 84 int &y=e[x][i].to; 85 if(is_centroid[y]||y==par) continue; 86 get_max(y,x,dep+1,dis+e[x][i].w); 87 } 88 } 89 int n; 90 std::deque<int> q; 91 inline bool check_subtree(const int x) { 92 max_depth=0; 93 for(unsigned i=0;i<e[x].size();i++) { 94 int &y=e[x][i].to; 95 if(is_centroid[y]) continue; 96 tmax_depth=0; 97 get_max(y,x,1,e[x][i].w); 98 q.clear(); 99 for(int i=0,j=max_depth;i<=tmax_depth;i++) { 100 while((i+j)>u) j--; 101 while((i+j)>=l) { 102 while(!q.empty()&&max[q.back()]<=max[j]) q.pop_back(); 103 q.push_back(j); 104 j--; 105 } 106 while(!q.empty()&&(q.front()+i)>u) q.pop_front(); 107 if(!q.empty()&&(max[q.front()]+tmax[i])>=0) { 108 return true; 109 } 110 } 111 for(int i=1;i<=max_depth;i++) { 112 max[i]=std::max(max[i],tmax[i]); 113 } 114 for(int i=max_depth+1;i<=tmax_depth;i++) { 115 max[i]=tmax[i]; 116 } 117 max_depth=std::max(max_depth,tmax_depth); 118 } 119 return false; 120 } 121 inline bool check() { 122 memset(is_centroid,0,sizeof is_centroid); 123 for(int i=1;i<=centroid[0];i++) { 124 is_centroid[centroid[i]]=true; 125 if(check_subtree(centroid[i])) return true; 126 } 127 return false; 128 } 129 int main() { 130 unsigned $=1<<23; 131 char *_=(char*)malloc($)+$; 132 asm("mov %0,%%esp"::"r"(_)); 133 n=getint(),l=getint(),u=getint(); 134 for(int i=1;i<n;i++) { 135 int u=getint(),v=getint(),w=getint(); 136 add_edge(u,v,w); 137 add_edge(v,u,w); 138 } 139 get_centroid(rand()%n+1,n); 140 double l=0,r=1e6; 141 while(l+eps<r) { 142 m=(l+r)/2; 143 (check()?l:r)=m; 144 } 145 printf("%.3f\n",l); 146 return 0; 147 }