保密
题解
还是蛮简单的一道题。
其实我们完全可以将原题拆分成两个部分,分别是求出起点到每个点的距离与最后的路径规划。
因为这两个部分的做法是基本没有什么联系的。
首先对于第一部分,它涉及到两个量的比值的计算,我们用
(
p
1
,
p
2
,
.
.
.
,
p
k
)
(p_{1},p_{2},...,p_{k})
(p1,p2,...,pk)来表示从起点到点
k
k
k所经过的边,我们要使得对于每一个点,
∑
i
=
1
k
t
p
i
∑
i
=
1
k
s
p
i
\frac{\sum_{i=1}^{k}t_{p_{i}}}{\sum_{i=1}^{k}s_{p_{i}}}
∑i=1kspi∑i=1ktpi最小。
这是不是一个很明显的0/1分数规划呀,我们只需要对于每一个点编号在
[
1
,
n
1
]
[1,n1]
[1,n1]中的点都去二分一下到它的答案比值即可。对的,你没看错,是每一个点
由于点数太多了,我们必须要用整体二分,你如果非要一个一个去二分的话说不定也能过。我们只需要将所有需要二分的点塞进一个序列,有点类似与快排一样去二分就行了。
至于如何判断一个点当前的二分值是否合法,可以从上式推的。假设
m
i
d
mid
mid是当前的二分值,
m
i
d
≥
∑
i
=
1
k
t
p
i
∑
i
=
1
k
s
p
i
⇔
∑
i
=
1
k
t
p
i
−
m
i
d
⋅
∑
i
=
1
k
s
p
i
≤
0
⇔
∑
i
=
1
k
(
t
p
i
−
m
i
d
⋅
s
p
i
)
≤
0
mid\geq \frac{\sum_{i=1}^{k}t_{p_{i}}}{\sum_{i=1}^{k}s_{p_{i}}} \Leftrightarrow \sum_{i=1}^{k}t_{p_{i}}-mid\cdot \sum_{i=1}^{k}s_{p_{i}}\leq 0\Leftrightarrow \sum_{i=1}^{k}(t_{p_{i}}-mid\cdot s_{p_{i}})\leq 0
mid≥∑i=1kspi∑i=1ktpi⇔∑i=1ktpi−mid⋅∑i=1kspi≤0⇔∑i=1k(tpi−mid⋅spi)≤0。
所以我们只需要将边权设为
t
i
−
m
i
d
⋅
s
p
i
t_{i}-mid\cdot s_{p_{i}}
ti−mid⋅spi,判断到每一个点的最短路的正负性即可。
至于这个最短路,很明显,如果我们用spfa,dijkstra,floyd,明显会T的,spfa运气好的话或许不会T,2011年的题应该不会卡spfa吧。。。。
观察到题目有强调图中不存在环,所以我们可以通过拓扑来求最短路。
拓扑序可以在预处理的时候就求出来,后面直接跑就可以了。
之后,我们就只需要解决如何让选入口了。
观察到每个空腔一定有且只有两个不在同一行的入口,这不是在极致暗示二分图吗。
我们很快就想到了类似文理分科的网络流模型。但观察到不同的空腔可能有同样的入口,不能直接用它来套用。
但我们可以对它进行一些更改,仍然使用最大流最小割的方式。
我们将一个这两排入口分别与源点与汇点相连,流量就是起点到它的危险性,我们先前二分出来的那个值。
我们将同意空腔的两个入口相连,它们一定是在不同的两排的,它们之间的连边为inf,保证这条边不会被切断,使最小割切断的一定是它的两个入口中的一个的边。
这样跑出来的最小割就一定将任意空腔的至少切断了一条边,满足每个空腔都会被探索。
至于最小割,根据最大流最小割定理,用dinic跑一道最大流即可求出。
无解的情况,只要判断一下是不是每个空腔都能被走到,其实只要看网络流最后跑出来的值是否不低于inf即可。
时间复杂度 O ( n 1 m l o g n + n 1 2 m 1 ) O\left(n_{1}mlog_{n}+n_{1}^2m_{1}\right) O(n1mlogn+n12m1),看起来好像过不了,但as is widely acknowledged,网络流的时间复杂度极度不准就对了。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 100005
#define reg register
typedef unsigned int LL;
const double eps=1e-7;
const double inf=0x7f7f7f7f7f7f;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,m,head[MAXN],tot,b[MAXN];double dis[MAXN],f[MAXN];
int sta[MAXN],stak,pos[MAXN],cntpos,deg[MAXN],cur[MAXN];
int U[MAXN],V[MAXN],n1,m1,S,T,cnt,dep[MAXN],ql[MAXN],qr[MAXN];
queue<int> q;
struct edge{int to,nxt;double up,down;}e[MAXN<<1];
struct ming{int a,b,t,s;}a[MAXN];
inline void addEdge(int u,int v,double w1,double w2){e[++tot]=(edge){v,head[u],w1,w2};head[u]=tot;}
struct Emiya{int to,nxt;double flow;int op;}ed[MAXN<<1];
inline void add_Edge(const int u,const int v,const double f){ed[++tot]=(Emiya){v,head[u],f};head[u]=tot;}
inline void addedge(const int u,const int v,const double f){add_Edge(u,v,f);ed[tot].op=tot+1;add_Edge(v,u,0);ed[tot].op=tot-1;}
inline void work(const double mid){
for(reg int i=1;i<n;++i)dis[i]=inf;dis[n]=0;
for(reg int i=1;i<=n;++i)
for(reg int j=head[pos[i]];j;j=e[j].nxt)
dis[e[j].to]=min(dis[e[j].to],dis[pos[i]]+e[j].up-e[j].down*mid);
}
inline void sakura(const double L,const double R,const int al,const int ar){
if(L+eps>R){for(reg int i=al;i<=ar;++i)f[b[i]]=L;return ;}
if(al>ar)return ;const double mid=(L+R)/2.0;work(mid);int nowl=0,nowr=0;
for(reg int i=al;i<=ar;++i)if(dis[b[i]]<=0)ql[++nowl]=b[i];else qr[++nowr]=b[i];
for(reg int i=al;i<al+nowl;++i)b[i]=ql[i-al+1];
for(reg int i=al+nowl;i<=ar;++i)b[i]=qr[i-al-nowl+1];
sakura(L,mid,al,al+nowl-1);sakura(mid+eps,R,al+nowl,ar);
}
inline void init(){
cntpos=0;stak=0;
for(reg int i=1;i<=n;++i)if(!deg[i])sta[++stak]=i;
while(stak){
const int x=sta[stak--];pos[++cntpos]=x;
for(reg int i=head[x];i;i=e[i].nxt){
int v=e[i].to;deg[v]--;
if(!deg[v])sta[++stak]=v;
}
}
}
inline bool bfs(){
for(reg int i=1;i<=cnt;++i)dep[i]=0,cur[i]=head[i];
while(!q.empty())q.pop();dep[S]=1;q.push(S);
while(!q.empty()){
const int u=q.front();q.pop();
for(reg int i=head[u];i;i=ed[i].nxt){
const int v=ed[i].to;
if(!dep[v]&&ed[i].flow>eps)
q.push(v),dep[v]=dep[u]+1;
}
}
return dep[T];
}
inline double dfs(const int u,double maxf){
if(u==T||maxf<eps)return maxf;double res=0;
for(reg int i=cur[u];i;i=ed[i].nxt){
cur[u]=i;const int v=ed[i].to;if(ed[i].flow<eps||dep[v]!=dep[u]+1)continue;
const double f=dfs(v,min(ed[i].flow,maxf));
ed[i].flow-=f;ed[ed[i].op].flow+=f;res+=f;maxf-=f;
}
return res;
}
inline double dosaka(){double ans=0;while(bfs())ans+=dfs(S,inf);return ans;}
signed main(){
read(n);read(m);for(reg int i=1;i<=n;++i)b[i]=i;
for(reg int i=1;i<=m;++i)read(a[i].a),read(a[i].b),read(a[i].t),read(a[i].s),
addEdge(a[i].a,a[i].b,1.0*a[i].t,1.0*a[i].s),++deg[a[i].b];
read(m1);read(n1);init();sakura(0,1e9,1,n1);f[n]=0;
for(reg int i=1;i<=m1;++i)read(U[i]),read(V[i]);
S=n1+1;cnt=T=n1+2;for(reg int i=1;i<=cnt;++i)head[i]=0;tot=0;
for(reg int i=1;i<=n1;++i)if(i&1)addedge(S,i,f[i]);else addedge(i,T,f[i]);
for(reg int i=1;i<=m1;++i)addedge(U[i],V[i],inf);const double ans=dosaka();
if(ans>1e9)puts("-1");else printf("%.1f\n",ans);
return 0;
}