【东方幻想乡系列模拟赛 Stage 4】 河城荷取

Description

在幻想乡,河城荷取是擅长高科技工业的河童。荷取的得意之作除了光学迷彩外,还有震动整个幻想乡的巨型人形『非想天则』。不过由于人形太过巨大,所以为它充能是一件很麻烦的事。人形一共有N个电能池,编号1..N。其中前L个电能池(即编号为1..L的电能池)连接着外部充能接口,而编号为N的电能池连接着动力炉核心。在N个蓄能池之间有M条单向管道,每条管道有一个激活代价cost和电能传输极限limit。当激活度达到某个值时,所以激活代价小于等于这个值的管道都会被激活,但是每一条管道只能够最多传送limit个单位的电能。外部接口到电能池和电能池到动力炉核心的管道传输没有限制并且激活代价为0。现在荷取想往动力炉核心输入至少K个单位的电能,求需要的最小激活度。

Input

第1行:4个正整数N,M,L, K 
第2..M+1行:4个整数,u,v,limit,cost,表示一条由u到v的管道,传输极限limit,激活代价为cost

Output

第1行:1个整数,表示最小激活代价

Sample Input

6 5 3 3 
1 4 2 4 
2 4 3 5 
3 5 4 2 
4 6 2 3 
5 6 3 4 

Sample Output

4 

Hint

对于30%的数据: 1 ≤ L ≤ N ≤ 100 ,0 ≤ M ≤ 2,000 ,1 ≤ cost ≤ 10,000 
对于60%的数据: 1 ≤ L ≤ N ≤ 1,000,0 ≤ M ≤ 20,000 ,1 ≤ cost ≤ 10,000 
对于100%的数据:1 ≤ L ≤ N ≤ 2,000,0 ≤ M ≤ 80,000 ,1 ≤ cost ≤ 1,000,000 
对于100%的数据:1 ≤ limit ≤ 1,000 

保证任意(u,v)都只出现一次


【分析】

离散化 + 二分答案 + 最大流

我们只需要二分激活度,然后最大流验证。注意可以离散化出现过的激活度,那么只需要二分数组下标即可。


【代码】

/*
    ID:Ciocio
	LANG:C++
	DATE:2013-11-23
	TASK:nitori
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

#define INF 999999999
#define MAXN 2005
#define MAXM 80005

int N,M,L,K,ans,Limit;
int vd[MAXN],dis[MAXN],Map[MAXN][MAXN],g[MAXN][MAXN];
int Y[MAXM+MAXN],Last[MAXM+MAXN],Next[MAXM+MAXN],Cost[MAXM+MAXN];
int V[MAXM],cnt;

int tot;
void _addedge(int a,int b,int c)
{
	tot++;
	Y[tot]=b;Cost[tot]=c;
	Next[tot]=Last[a];
	Last[a]=tot;
}

void _read(int &x)
{
	char tt=getchar();
	while(tt<'0'||'9'<tt) tt=getchar();
	for(x=0;'0'<=tt&&tt<='9';x=x*10+tt-'0',tt=getchar());
}

void _init()
{
	_read(N);_read(M);_read(L);_read(K);
	for(int i=1;i<=M;i++)
	{
		int u,v,limit,cost;
		_read(u);_read(v);_read(limit);_read(cost);
		_addedge(u,v,cost);
		Map[u][v]=limit;
		V[++cnt]=cost;
	}
	for(int i=1;i<=L;i++)
	{
	    _addedge(0,i,0);
		Map[0][i]=INF;
	}
	V[++cnt]=0;
	N++;
	_addedge(N-1,N,0);
	Map[N-1][N]=INF;
}

int _SAP(int u,int flow)
{
	if(u==N) return flow;
	int delta=0;
	for(int j=Last[u];j;j=Next[j])
	{
		int v=Y[j];
		if(Cost[j]<=Limit&&g[u][v]>0&&dis[u]==dis[v]+1)
		{
			int temp=_SAP(v,min(flow-delta,g[u][v]));
			g[u][v]-=temp;
			g[v][u]+=temp;
			delta+=temp;
			if(delta==flow) return delta;
		}
	}
	if(dis[0]>=N) return delta;
	vd[dis[u]]--;
	if(vd[dis[u]]==0) dis[0]=N;
	dis[u]++;
	vd[dis[u]]++;
	return delta;
}

void _solve()
{
	sort(V+1,V+1+cnt);
	int j=0;
	for(int i=1;i<=cnt;i++)
		if(V[i]!=V[i+1])
			V[++j]=V[i];
	cnt=j;
	int Left=1,Right=cnt,Mid,temp;
	while(Left<=Right)
	{
		temp=0;
		Mid=(Left+Right)>>1;
		Limit=V[Mid];
	    memset(vd,0,sizeof(vd));
	    memset(dis,0,sizeof(dis));
	    memcpy(g,Map,sizeof(Map));
	    while(dis[0]<N)
			temp+=_SAP(0,INF);
		if(temp>=K)
		{
			ans=Limit;
			Right=Mid-1;
		}
		else Left=Mid+1;
	}
	printf("%d\n",ans);
}

int main()
{
	_init();
	_solve();
	return 0;
}


展开阅读全文

没有更多推荐了,返回首页