[SDOI2011]保密

保密

题解

还是蛮简单的一道题。

其实我们完全可以将原题拆分成两个部分,分别是求出起点到每个点的距离与最后的路径规划。
因为这两个部分的做法是基本没有什么联系的。

首先对于第一部分,它涉及到两个量的比值的计算,我们用 ( 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=1kspii=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 midi=1kspii=1ktpii=1ktpimidi=1kspi0i=1k(tpimidspi)0
所以我们只需要将边权设为 t i − m i d ⋅ s p i t_{i}-mid\cdot s_{p_{i}} timidspi,判断到每一个点的最短路的正负性即可。

至于这个最短路,很明显,如果我们用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;
}

谢谢

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值