建图相对论

luogu2754 家园

网络流大神题

刚看到这道题的一瞬间我就知道我跪掉了,完全没思路啊。

挣扎了一个多小时愣是想不出来 ,看题解。感叹这题太牛逼的话我就不多说了,讲一下思路吧。

我们做这个题要有两个知识储备:

1.爱因斯坦的相对论

2.分层建图的思想

关于相对论我就不多科普了,但是这个题的确要用到类似的思想。举个栗子,这一秒的asia已经不是上一秒的asia了,他有可能更胖了,也有可能更神了,对不对?(手动@victorique)

放到这道题来说,就是我们可以把每一个单位时间的太空站看做不同的太空站。对于一个时刻的太空站上面的人来说,下一个时刻他可以留在太空站,也可以乘坐飞船到下一个太空站。所以我们要把每一个时刻的太空站与下一个时刻的太空站连一条inf的边,然后和下一时刻的下一站连一条为飞船承载人数的边。

这就要用到分层建图的思想。我们把这个图看做分层的,每一层都有一个自己的源点和汇点。然后从超级源向每一层的源点连一条inf,从超级汇向每一层的汇点连一条inf,就OK了。然后我们枚举每一天,看看这一天能不能有超过k个人到达汇点,如果有就退出。

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define big (1000000000 + 10)
#define maxn (100000 + 10)
using namespace std;
int dis[maxn],head[maxn],cur[maxn];
int cnt,n,m,k,ans,tot,s,t=1001;
int read(){
	char c;int r=0,f=1;
	c=getchar();
	while (c<'0' || c>'9'){
		if (c=='-') f=-1;
		c=getchar();
	}
	while (c>='0' && c<='9'){
		r=(r*10)+(c^48);
		c=getchar();
	}
	return r*f;
}
struct node{
	int key,num;
	int round[20];
}p[maxn];
struct edge{
	int next,to,dis;
}ljb[maxn];
void add_edge(int x,int y,int z){
	ljb[++cnt].next=head[x];
	ljb[cnt].to=y;
	ljb[cnt].dis=z;
	head[x]=cnt;
}
void add(int x,int y,int z){
	add_edge(x,y,z);
	add_edge(y,x,0);
}
queue<int> q;
bool bfs(){
	for (int i=s;i<=t;i++) dis[i]=-1;
	q.push(s); dis[s]=0;
	while (!q.empty()){
		int x=q.front(); q.pop();
		for (int i=head[x];i!=-1;i=ljb[i].next){
			int y=ljb[i].to;
			if (dis[y]==-1 && ljb[i].dis>0){
				dis[y]=dis[x]+1;
				q.push(y);
			}
		}
	}
	if (dis[t]!=-1) return 1;
	return 0;
}
int dfs(int x,int low){
	if (x==t) return low;
	for (int i=head[x];i!=-1;i=ljb[i].next){
		int y=ljb[i].to;
		if (dis[y]==dis[x]+1 && ljb[i].dis>0){
			int flow=dfs(y,min(low,ljb[i].dis));
			if (flow){
				ljb[i].dis-=flow;
				ljb[i^1].dis+=flow;
				return flow;
			}
		}
	}
	return 0;
}
int main(){
	n=read();m=read();k=read();
	n+=2; 
	for (int i=1;i<=m;i++){
		p[i].key=read();p[i].num=read();
		for (int j=0;j<p[i].num;j++){
			p[i].round[j]=read();
			p[i].round[j]+=2;
		}
	}
	for (int i=s;i<=t;i++) head[i]=-1;cnt=-1;
	int day=0;
	while (day<30){
		add(day*n+1,t,big);
		add(s,day*n+2,big);
		if (day){
			for (int i=1;i<=n;i++) add((day-1)*n+i,day*n+i,big);
			for (int i=1;i<=m;i++){
				int x=p[i].round[(day-1)%p[i].num];
				int y=p[i].round[day%p[i].num];
				add((day-1)*n+x,day*n+y,p[i].key); 
			} 
			while (bfs()){
				for (int i=s;i<=t;i++) cur[i]=head[i];
				do{
					tot=dfs(s,big);
					ans+=tot;
				}
				while (tot);
			}
		}
		if (ans>=k) break;
		++day;
	}
	printf("%d",(day==30)?0:day);
}
赞 赏
蒟蒻的邮箱:xmzl200201@126.com