[国家集训队]航班安排

航班安排

题解

很简单的一道网络流

看到这道题应该是十分容易想起费用流。
很明显,如果我们将每个单位的时间的机场分别建点再出来连的话是明显会T的,因为这样边的数量达到了 n ( T + m ) n(T+m) n(T+m)的级别。
考虑不通过机场来建点,通过询问来建点。
将每个询问拆成入点与出点,两者之间连流量为 1 1 1,费用为 − c -c c的边,再在所有的询问之间判断哪些出点能够到达哪些入点,在它们间连边。最后再连出起点与终点的边即可。
至于总共 K K K台飞机的限制,只要再加一个超源点,将它与源点之间边的流量设为 K K K即可。

这种方法建出来点数是 2 m + 4 2m+4 2m+4,边数是 m 2 m^2 m2级别的。
时间复杂度 O ( K m 3 ) O\left(Km^3\right) O(Km3),当然,如果它卡了spfa的话。但事实证明没有

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 205
#define MAXT 3005
#define MAXM 505
#define MAXX 250005
#define reg register
typedef long long LL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
const LL inf=0x7f7f7f7f7f7f;
template<typename _T>
inline 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,K,Ti,head[MAXM],tot,pre[MAXM],pw[MAXM],dis[MAXM],cnt,S1,T1;
int tim[MAXN][MAXN],pai[MAXN][MAXN],S,T,fl[MAXM];bool link[MAXN][MAXT];
queue<int> q;bool vis[MAXM];
struct edge{int to,nxt,flow,paid,op;}e[MAXX];
struct ming{int a,b,s,t;}s[MAXN];
inline void addEdge(const int u,const int v,const int w,const int f){e[++tot]=(edge){v,head[u],f,w};head[u]=tot;}
inline void addedge(const int u,const int v,const int f,const int w){addEdge(u,v,w,f);e[tot].op=tot+1;addEdge(v,u,-w,0);e[tot].op=tot-1;}
inline pii EK(){
	int mf=0,res=0;
	while(1){
		while(!q.empty())q.pop();
		for(reg int i=1;i<=cnt;++i)dis[i]=INF,fl[i]=0;
		q.push(S);dis[S]=0;vis[S]=1;fl[S]=INF;
		while(!q.empty()){
			const int u=q.front();q.pop();vis[u]=0;if(!fl[u])continue;
			for(reg int i=head[u];i;i=e[i].nxt){
				const int v=e[i].to;
				if(dis[v]>dis[u]+e[i].paid&&e[i].flow>0){
					dis[v]=dis[u]+e[i].paid;pre[v]=u;pw[v]=i;
					if(!vis[v])q.push(v),vis[v]=1,fl[v]=min(fl[u],e[i].flow);
				}
			}
		}
		if(dis[T]>INF-1)break;mf+=fl[T];res+=fl[T]*dis[T];
		for(reg int i=T;i^S;i=pre[i])e[pw[i]].flow-=fl[T],e[e[pw[i]].op].flow+=fl[T];
	}
	return make_pair(mf,res);
}
signed main(){
	read(n);read(m);read(K);read(Ti);S=m*2+1;T=m*2+2;S1=m*2+3;cnt=T1=m*2+4;
	for(reg int i=0;i<n;++i)for(reg int j=0;j<n;++j)read(tim[i][j]);
	for(reg int i=0;i<n;++i)for(reg int j=0;j<n;++j)read(pai[i][j]);
	for(reg int i=1;i<=m;++i){
		int c;read(s[i].a),read(s[i].b),read(s[i].s);read(s[i].t);read(c);
		if(tim[0][s[i].a]>s[i].s||s[i].t+tim[s[i].b][0]>Ti)continue;
		addedge(S1,2*i-1,1,pai[0][s[i].a]);addedge(2*i,T1,1,pai[s[i].b][0]);addedge(2*i-1,2*i,1,-c);
		for(reg int j=1;j<i;++j){
			if(s[i].s-tim[s[j].b][s[i].a]>=s[j].t)addedge(2*j,2*i-1,1,pai[s[j].b][s[i].a]);
			if(s[i].t+tim[s[i].b][s[j].a]<=s[j].s)addedge(2*i,2*j-1,1,pai[s[i].b][s[j].a]);
		} 
	}
	addedge(S,S1,K,0);addedge(T1,T,K,0);
	pii ans=EK();printf("%d\n",-ans.second);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
后缀自动机被广泛应用于OI竞赛中,特别是在字符串相关的问题中。它是一种高效的数据结构,能够有效地解决各种字符串匹配、模式匹配和计数等问题。 首先,后缀自动机可以用于解决最长公共子串和最长公共子序列等问题。对于给定的两个字符串,可以将其加入到后缀自动机中,并通过动态规划的方式求解最长公共子串或子序列的长度。 其次,后缀自动机还可以用于解决多次询问下的子串出现次数问题。通过构建全局后缀自动机,可以在O(n)的时间复杂度内预处理字符串,并在O(m)的时间复杂度内得出任意子串的出现次数,其中n为字符串长度,m为询问总数。 另外,后缀自动机还可以用于解决包含多模式匹配的问题。通过将模式串加入到后缀自动机中,并预处理自动机的fail指针,可以在O(n)的时间复杂度内找到所有模式串在文本中的出现位置。这在处理大规模的文本匹配问题时非常有用。 此外,后缀自动机还可以进行字符串的字典序统计。通过在构建自动机时记录每个节点的信息,可以在O(n)的时间复杂度内得到字符串的字典序第k小/大的子串。 总之,后缀自动机在OI竞赛中有着广泛的应用,能够解决各种字符串相关的问题。通过巧妙地构建自动机,并充分利用其性质,可以实现高效的字符串算法,为解决复杂的字符串问题提供了有力的工具。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值