[关闭]
@xiaoziyao 2021-07-13T22:48:26.000000Z 字数 3108 阅读 971

CF1307G Cow and Exercise解题报告

解题报告


CF1307G Cow and Exercise解题报告:

更好的阅读体验

题意

给定一个 个点 条边的带边权有向图, 次独立的询问一个非负整数 ,每次可以支持将若干条边的边权增加任意非负实数值,但是需要保证这些值之和不超过 ,你要使得 的最短路长度最长。(

分析

毒瘤线性规划对偶题,这种题不*3500天理难容/fn/fn/fn。

考虑设 为有向边的边权, 表示有向边 增加的边权, 的最短路(注意,这里的 类似差分约束中的最短路,源点 值可以大于 ),那么可以列出最短路对应的线性规划式:

根据线性规划拼凑的思想,考虑使用 个第二行式子, 个第三行式子, 个第四行式子凑出 的系数,然后用右边的上界之和代替 ,再顺便变一下形:

此时,我们就可以其对偶形式表示出来了。

由于 的限制很少,而 要尽量小,所以我们可以直接调整 的值使得

这个形式仍然不是很好做,考虑将它变换成费用流的形式,即给 同时乘上流量

我们对于原图的边 在费用流中建一条边 ,容量为 ,费用为 ,这样对于任意一组合法的流量,都会满足上面第二条限制(反之亦然),且其费用

而且此时存在一个优美的性质 ,即 ,那么最后的答案为:

我们不可能枚举所有的合法流量组,因此考虑建出 对应的凸壳,即所有 的凸壳。

我们发现在最小费用最大流不断增广的时候的时候, 单调递增,同时根据最小费用最大流的贪心性,也可以发现 是不减的,那么每一次增广结束后的 一定不重不漏地构成了凸壳的所有位置。

于是我们建图后跑出凸壳,每次查询的时候枚举凸壳所有位置并取最小值就可以了。

时间复杂度:

代码

  1. #include<stdio.h>
  2. #include<queue>
  3. #include<vector>
  4. #define inf 1000000000
  5. using namespace std;
  6. const int maxn=55,maxm=5005;
  7. int n,m,Q,e,flow,cost;
  8. int start[maxn],to[maxm],then[maxm],limit[maxm],worth[maxm],rev[maxm],dis[maxn],vis[maxn],rec[maxn];
  9. queue<int>q;
  10. vector< pair<int,int> >v;
  11. inline int min(int a,int b){
  12. return a<b? a:b;
  13. }
  14. inline void add(int x,int y,int z,int w,int r){
  15. then[++e]=start[x],start[x]=e,to[e]=y,limit[e]=z,worth[e]=w,rev[e]=r;
  16. }
  17. inline void addedge(int x,int y,int z,int w){
  18. add(x,y,z,w,e+2),add(y,x,0,-w,e);
  19. }
  20. int check(int s,int t){
  21. for(int i=1;i<=n;i++)
  22. dis[i]=inf,vis[i]=0;
  23. while(!q.empty())
  24. q.pop();
  25. dis[s]=0,vis[s]=1,q.push(s);
  26. while(!q.empty()){
  27. int x=q.front();
  28. vis[x]=0,q.pop();
  29. for(int i=start[x];i;i=then[i]){
  30. int y=to[i];
  31. if(limit[i]&&dis[y]>dis[x]+worth[i]){
  32. dis[y]=dis[x]+worth[i],rec[y]=i;
  33. if(vis[y]==0)
  34. vis[y]=1,q.push(y);
  35. }
  36. }
  37. }
  38. return dis[t]==inf? 0:1;
  39. }
  40. void work(int s,int t){
  41. int minn=inf;
  42. for(int i=t;i!=s;i=to[rev[rec[i]]])
  43. minn=min(minn,limit[rec[i]]);
  44. flow+=minn;
  45. for(int i=t;i!=s;i=to[rev[rec[i]]])
  46. limit[rec[i]]-=minn,limit[rev[rec[i]]]+=minn,cost+=worth[rec[i]]*minn;
  47. }
  48. int main(){
  49. scanf("%d%d",&n,&m);
  50. for(int i=1;i<=m;i++){
  51. int x,y,z;
  52. scanf("%d%d%d",&x,&y,&z);
  53. addedge(x,y,1,z);
  54. }
  55. while(check(1,n))
  56. work(1,n),v.push_back(make_pair(cost,flow));
  57. scanf("%d",&Q);
  58. while(Q--){
  59. int x;
  60. double ans=1.0*inf;
  61. scanf("%d",&x);
  62. for(int i=0;i<v.size();i++)
  63. ans=min(ans,1.0*(v[i].first+x)/v[i].second);
  64. printf("%.10lf\n",ans);
  65. }
  66. return 0;
  67. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注