醉熏熏的幻想乡

题解

相当阴间的一道题

首先如果只用解决第一小问的话,我们很容易想到通过网络流来求最大流的。
但是我们现在还要保证总费用最小呀,这该怎么办?拆成许多个点费用流,不TTT才怪。
我们假设图是一个完全二分图(就是左侧所有点与右侧所有点间都有边),我们是不是可以贪心地解决。
我们考虑对于每个极小单位流量,都选择当前最优的一个点选择,那么我们每次选择的点就是当前流量最小的点。
也就是说,我们选择的这个点当前的导数最小的点,这样的话,我们选择到最后,一定是所有选择了的点导数全部一样,而所有没有选择的点,导数肯定不小于选择了的点的导数。
我们原式是一个二次函数呀,那它的导数也就是一个一次函数,其现在的真实值就是将它现在的产量带入这个一次函数嘛。
那样的话岂不是就可以直接二分出我们这个导数的使它跑满的最大值,也就知道现在每个点的贡献,可以计算答案了。

でも(demo),这是对于一个完全二分图呀,我们的图不一定是一个完全二分图呀。
也就是说,实际上并不是每一个函数最后的导数都相同。
不过我们可以发现,如果我们记f(λ)f(\lambda)f(λ)表示我们导数的最大值为λ\lambdaλ,我们的最大流量的话,我这个f(λ)f(\lambda)f(λ)应该是一个O(n)O(n)O(n)级别分段的分段一次函数。
我们先就按原本最大流(最优费用时的流的状况)时,每个点对每个点的流量贡献来看每个点的流量填充情况,显然,在我们的λ\lambdaλ没有达到当前函数导数最终的最大值时,它是不会影响到别的点的贡献流量的。如果达到最大值后,即使我们λ\lambdaλ的增长,它也不会对其他点产生新的贡献,不然的话最大流的情况下它为什么不贡献呀。
也就是说,当我们这样划分后,不同点的贡献就独立开来了。
它的贡献会在我们的λ\lambdaλ达到某个阈值时开始增长,达到某个阈值后停止增长。
而它在阈值内的增长,显然单位费用是关于一个流量的一次函数,也就是原二次函数的导。那么我们将单位费用设为自变量,那么流量也肯定是一个一次函数。
只看一个点,它关于λ\lambdaλ变化,肯定是一个一次函数嘛。当然是一个分333段的一次函数 ,具体怎么描述好像笔者画不来图。
既然一个点的f(λ)f(\lambda)f(λ)是一个分段一次函数,那么我们将所有点的f(λ)f(\lambda)f(λ)累加起来,也应该是一个分段的一次函数,并且总的分段是O(n)O\left(n \right)O(n)级别的。
我们将这个分段函数算出来不就可以积分算答案了吗?

但这么算这个分段函数呢?
首先,我们可以想到一种方法算每个单点处的函数长相。
假设我们要算的是费用为λ\lambdaλ处的fff函数长啥样,我们可以这样做。
我们将所有点都向SSS连一条它的导数在不超过λ\lambdaλ情况下的最多贡献的流量,然后跑最大流,我们看我们现在的割边情况。
如果我们现在左侧点到SSS的边被割掉了,也就是说这个点的流量在这个点是满的嘛,那么就会贡献到我们fff函数的常数项上。
但如果流满了,但是它本身还可以继续流呢,也就是cic_ici​没满。那不就是我们在增加2ai2a_i2ai​的费用,就会增加111的流量嘛(只看这个点的趋势),那肯定就是给fff加上一个一次函数呀。
如果它没被割掉,那割掉的肯定是右侧点嘛,右侧点既然流满了,那也就说明它是一个常数项,直接加到fff的常数项上。
这样不就求出了我们单点上的函数了嘛。
但我们怎么求整个分段函数呢?不妨分治处理。
如果我们左端点与右端点的函数不一样,那不就说明他们之间有一个新的函数。
我们可以将左右函数的交点拿出来,在这个交点这里往两边二分。
不过如果是看两边函数是不是一样的可能有点问题,其实应该是将它们的交点的左侧函数与右侧函数求出来,看是否与原来的左函数与右函数一样。

这样我们就轻松的求出了整个分段函数的长相,然后就可以积分算答案了。
不过由于都是一次函数,根本没必要积分,小学数学就可以算面积了。

时间复杂度O(n∣网络流∣)\left(n|网络流|\right)(n∣网络流∣),我们分治其实也只求了端点处的函数,所以是O(n)O\left(n\right)O(n)级别的。

番外

代码在下面,这里讲一个从老师那里听来的故事(真实性不可考)
众所周知,ZJOI2015 是东方场 这就是OIer界车万的力量吗。
本题出题人clj理所当然是一个车万。
据说,当年在clj初二的时候,他还不是像现在这样强大。当时,笔者学校有一个非常强的OIer,这个OIer是一个车万,它出题经常都会带许多东方背景。
而clj当时与这个OIer有许多接触(或许是崇拜?),然后就理所当然得沾染了许多东方的元素,这对他以后的出题风格造成了许多影响,导致他也出了许多东方风格的题。
于是,就有了我们现在所看到的ZJOI2015。
不要再永琳(erin)啦!关于网易云循环えーりん!えーりん!助けてえーりん!的这码子事。

源码

几乎是照着hehezhou\rm h\red{ehezhou}hehezhou代码写的,因为基本上都是从代码中学到做法。不是我讽刺他的题解,两者参照啦!!!

#include<bits/stdc++.h>
using namespace std;
#define MAXN 105
#define MAXM 205
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
typedef long double Ld;
typedef pair<LL,LL> pii;
const double INF=1e9;
const int mo=1e9+7;
const int mod=1e6+7;
const int inv2=499122177;
const int inv3=332748118;
const int jzm=2333;
const int zero=2000;
const int n1=100;
const int lim=100000;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double feps=1e-11;
const double eps=1e-9;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){_T f=1;x=0;char s=getchar();while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}x*=f;
}
template<typename _T>
void print(_T x){if(x<0)putchar('-'),print(-x);if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,m,a[MAXN],b[MAXN],c[MAXN],d[MAXN];
int cnt,S,T,dis[MAXM],head[MAXM],tot,cur[MAXM];
bool mp[MAXN][MAXN];queue<int>q;
struct frac{LL x,y;frac(){x=0;y=1;}frac(LL X,LL Y){LL D=gcd(X,Y);x=X/D;y=Y/D;}frac operator * (const frac &rhs)const{return frac(x*rhs.x,y*rhs.y);}frac operator / (const frac &rhs)const{return frac(x*rhs.y,y*rhs.x);}frac operator + (const frac &rhs)const{return frac(x*rhs.y+y*rhs.x,y*rhs.y);}frac operator - (const frac &rhs)const{return frac(x*rhs.y-y*rhs.x,y*rhs.y);}bool operator == (const frac &rhs)const{return x==rhs.x&&y==rhs.y;}double getVal(){return 1.0*x/y;}
}ans;
typedef pair<frac,frac>pff;
struct edge{int to,nxt;double flow;int op;}e[MAXM*MAXM];
void addEdge(int u,int v,double f){e[++tot]=(edge){v,head[u],f};head[u]=tot;}
void addedge(int u,int v,double f){addEdge(u,v,f);e[tot].op=tot+1;addEdge(v,u,0);e[tot].op=tot-1;
}
bool bfs(){for(int i=1;i<=cnt;i++)cur[i]=head[i],dis[i]=0;while(!q.empty())q.pop();q.push(S);dis[S]=1;while(!q.empty()){int u=q.front();q.pop();for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;if(e[i].flow<feps||dis[v])continue;dis[v]=dis[u]+1;q.push(v);}}return dis[T]>0;
}
double dfs(int u,double maxf){if(u==T)return maxf;double res=0;for(int i=cur[u];i;cur[u]=i=e[i].nxt){int v=e[i].to;if(e[i].flow<feps||dis[v]!=dis[u]+1)continue;double tmp=dfs(v,min(maxf,e[i].flow));e[i].flow-=tmp;maxf-=tmp;res+=tmp;e[e[i].op].flow+=tmp;if(maxf<feps)break;}return res;
}
double dinic(){double res=0;while(bfs())res+=dfs(S,INF);return res;}
pff work(double x){for(int i=1;i<=cnt;i++)head[i]=0;tot=0;for(int i=1;i<=n;i++){if(!a[i]){if(b[i]<x)addedge(S,i,1.0*c[i]);}else if(b[i]<x){double up=(x-b[i])/(a[i]+a[i]);addedge(S,i,min(up,1.0*c[i]));}}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(mp[i][j])addedge(i,j+n,INF);for(int i=1;i<=m;i++)addedge(i+n,T,d[i]);frac K=frac(0,1),B=frac(0,1);double tmp=dinic();for(int i=1;i<=n;i++)if(!dis[i]){if(!a[i]){if(b[i]<x)B=B+frac(c[i],1);}else if(b[i]<x){if(2.0*a[i]*c[i]+b[i]>x)K=K+frac(1,a[i]+a[i]),B=B-frac(b[i],a[i]+a[i]);else B=B+frac(c[i],1);}}for(int i=1;i<=m;i++)if(dis[i+n])B=B+frac(d[i],1);return mkpr(K,B);
}
vector<pff>vec;
void sakura(pff l,pff r){if(l.fir==r.fir&&l.sec==r.sec)return ;frac px=(l.sec-r.sec)/(r.fir-l.fir);double tx=px.getVal();pff tmpl=work(tx-eps),tmpr=work(tx+eps);if(tmpr.fir==r.fir&&tmpr.sec==r.sec)vec.pb(mkpr(px,tmpl.sec+tmpl.fir*px));else sakura(l,tmpl),sakura(tmpr,r);
}
int main(){read(n);read(m);cnt=n+m;S=++cnt;T=++cnt;for(int i=1;i<=n;i++)read(a[i]),read(b[i]),read(c[i]);for(int i=1;i<=m;i++)read(d[i]);for(int i=1;i<=n;i++)for(int j=1,x;j<=m;j++)read(x),mp[i][j]=x;   work(1+eps);pff sum=work(1e9);printf("%lld\n",sum.sec.x);vec.pb(mkpr(frac(0,1),frac(0,1)));for(int i=1;i<=3;i++){pff l=work(i-1+eps),r=work(i-eps);sakura(l,r);vec.pb(mkpr(frac(i,1),r.sec+frac(i,1)*r.fir));}sakura(work(3+eps),sum);int siz=vec.size();for(int i=1;i<siz;i++){pff tmpl=work(vec[i].fir.getVal()-eps);pff tmpr=work(vec[i].fir.getVal()+eps);pff tp=work(vec[i-1].fir.getVal()+eps);ans=ans+vec[i].fir*(vec[i].fir*(tmpr.fir-tmpl.fir)+tmpr.sec-tmpl.sec);ans=ans+(vec[i].sec-vec[i-1].fir*tp.fir-tp.sec)*frac(1LL,2LL)*(vec[i].fir+vec[i-1].fir);}if(ans.x<0)ans.x=-ans.x,ans.y=-ans.y;printf("%lld/%lld\n",ans.x,ans.y);return 0;
}

谢谢!!!

[ZJOI2015]醉熏熏的幻想乡相关推荐

  1. [Zjoi2015]诸神眷顾的幻想乡

    [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 1537  Solved: 892 Description 幽香 ...

  2. 【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机

    [BZOJ3926][Zjoi2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝 ...

  3. bzoj 3926 [Zjoi2015]诸神眷顾的幻想乡

    3926: [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec   Memory Limit: 512 MB Submit: 1381   Solved: 811 [ Subm ...

  4. bzoj 3926: [Zjoi2015]诸神眷顾的幻想乡(广义后缀自动机)

    3926: [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec   Memory Limit: 512 MB Submit: 1009   Solved: 596 [ Subm ...

  5. [ZJOI2015] 诸神眷顾的幻想乡

    P3256[ZJOI2015 Day1]诸神眷顾的幻想乡 时间限制 : 20000 MS   空间限制 : 524288 KB 问题描述 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生 ...

  6. [bzoj3926][Zjoi2015]诸神眷顾的幻想乡

    来自FallDream的博客,未经允许, 请勿转载,谢谢. 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情 ...

  7. 【刷题】BZOJ 3926 [Zjoi2015]诸神眷顾的幻想乡

    Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看. ...

  8. [洛谷P3346][ZJOI2015]诸神眷顾的幻想乡

    题目大意:给你一棵$n$个点的树,最多有$20$个叶子节点,问共有几个不同的子串 题解:广义$SAM$,对每个叶子节点深搜一次,每个节点的$lst$设为这个节点当时的父亲,这样就可以时建出来的$SAM ...

  9. Luogu P3346 [ZJOI2015]诸神眷顾的幻想乡 广义SAM 后缀自动机

    题目链接 \(Click\) \(Here\) 真的是好题啊-不过在说做法之前先强调几个自己总是掉的坑点. 更新节点永远记不住往上跳\(p = fa[p]\) 新建节点永远记不住\(len[y] = ...

  10. BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡

    这道题首先要读明白题,就是说叶子节点不超过20个,那么我们就可以以每一个叶子节点为根建一个广义后缀自动机,这样就一定能表示出来所有的子串,然后统计一下答案就可以啦. (广义后缀自动机就是把好多串放到一 ...

最新文章

  1. Qt Designer提升控件
  2. SpringBoot不支持webapp的解决办法
  3. idea 升级到2020后 无法启动_IDEA 2020 无法启动的解决办法(启动崩盘)附IDEA 2020 新功能...
  4. 如何通过一个编程获得IP地址归属地的最简单办法
  5. asp.net mysql 中文乱码_mysql4导入mysql5中文乱码问题
  6. 有关python的知识点_Python常见的知识点整理
  7. HDU1407 测试你是否和LTC水平一样高【暴力】
  8. EasyUI-增删改操作
  9. nios IIcommand shell 烧录
  10. 互联网晚报 | 12月7日 星期二 | 阿里新设两大数字商业板块;B站宣布迈入8K超高清时代;中国物流集团正式成立...
  11. 使用Sencha cmd安装extjs6
  12. ModelState.IsValid 验证时忽略某些字段验证
  13. 期待可能性理论的司法适用
  14. Vue 实现PC端和移动端的自适应
  15. 测试、发布、小组合作及经验启示
  16. k8s控制器Deployment使用详解
  17. 张伟计算机基础案例教程课后答案,案例-大学计算机基础.doc
  18. 深蓝学院-多传感器融合定位课程-第5章-惯性导航原理及误差分析
  19. 一个无名内隐类的问题(Java)
  20. mysql 16进制字符串转中文_mysql如何把16进制转换成中文字符显示

热门文章

  1. 电脑蓝牙耳机,蓝牙耳机,详细教您蓝牙耳机怎么连接电脑
  2. 考研380分什么水平计算机,考研总分500考380难吗 考研380分是什么水平
  3. Linux---Apache网页优化---网页压缩
  4. YOLOv5的head详解
  5. PHP AES 加密解密实现
  6. 整理了500万+微信红包封面,速抢!
  7. Android高德地图基本开发/在线高德离线地图开发/断网使用离线地图(Assets文件夹的使用)
  8. pytorch版本下的yolov3训练实现火焰检测
  9. 习题 6.10 有一篇文章,共有三行文字,每行有80个字符。要求分别统计出其中英文大写字母、小写字母、数字、空格以及其他字符的个数。
  10. windows操作系统知识最全