CF536D. Tavas in Kansas

题目链接
对于博弈问题考虑倒着DP,首先把 s , t s,t s,t到每个点的最短路求出来离散化一下,设 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1] s s s取完距离它前 i i i近的点, t t t取完距离它前 j j j近的点后, s / t s/t s/t先手,之后对于 s / t s/t s/t的最优解,转移时枚举 s / t s/t s/t取到哪,显然可以前缀 m i n min min优化,效率做到 O ( n 2 ) O(n^{2}) O(n2)
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2005,M=2e5+5;
int n,m,S,T,a[N],hd[N],to[M],nx[M],wl[M],tt;
void add(int u,int v,int w){
	nx[++tt]=hd[u]; to[hd[u]=tt]=v; wl[tt]=w;
}
ll d[2][N],F=1e15;
struct node{
	int x; ll d;
	bool operator < (const node& r) const{
		return d>r.d;
	};
};
priority_queue<node>q;
void bfs(int s,bool p){
	for(int i=1;i<=n;i++) d[p][i]=F;
	d[p][s]=0; q.push((node){s,0});
	while(!q.empty()){
		node u=q.top(); q.pop();
		if(d[p][u.x]!=u.d) continue;
		for(int e=hd[u.x];e;e=nx[e]){
			int v=to[e]; if(u.d+wl[e]>=d[p][v]) continue;
			d[p][v]=u.d+wl[e]; q.push((node){v,d[p][v]});
		}
	}
	ll q[N]; memcpy(q,d[p],sizeof(d[p])); sort(q+1,q+n+1);
	//cout<<"p="<<p<<endl;
	for(int i=1;i<=n;i++) d[p][i]=lower_bound(q+1,q+n+1,d[p][i])-q;//cout<<d[p][i]<<" "; puts("");
}
int ms[N][N],mt[N][N];
ll ss[N][N],st[N][N],f[N][N],g[N][N],mf[N][N],mg[N][N];
int main()
{
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	cin>>n>>m>>S>>T;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int u,v,w,i=1;i<=m;i++)
		scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
	bfs(S,0); bfs(T,1);
	for(int i=0;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(d[0][j]>i) st[i][d[1][j]]+=a[j],mt[i][d[1][j]-1]=d[1][j];
			if(d[1][j]>i) ss[d[0][j]][i]+=a[j],ms[d[0][j]-1][i]=d[0][j];
		}
		for(int j=1;j<=n;j++) st[i][j]+=st[i][j-1],ss[j][i]+=ss[j-1][i];
		for(int j=n;~j;j--){
			if(!mt[i][j]) mt[i][j]=mt[i][j+1];
			if(!ms[j][i]) ms[j][i]=ms[j+1][i];
		}
		/*cout<<"i="<<i<<endl;
		for(int j=1;j<=n;j++) cout<<mt[i][j]<<" "; cout<<endl;
		for(int j=1;j<=n;j++) cout<<st[i][j]<<" "; cout<<endl;
		for(int j=1;j<=n;j++) cout<<ms[j][i]<<" "; cout<<endl;
		for(int j=1;j<=n;j++) cout<<ss[j][i]<<" "; cout<<endl;*/
	}
	//cout<<ms[4][0]<<endl;
	for(int i=n;~i;i--) for(int j=n;~j;j--){
		if(ms[i][j]) f[i][j]=-ss[i][j]-mg[ms[i][j]][j];
		mf[i][j]=f[i][j]-st[i][j];
		if(j<n) mf[i][j]=min(mf[i][j],mf[i][j+1]);
		if(mt[i][j]) g[i][j]=-st[i][j]-mf[i][mt[i][j]];
		mg[i][j]=g[i][j]-ss[i][j];
		if(i<n) mg[i][j]=min(mg[i][j],mg[i+1][j]);
		//printf("i=%d j=%d f=%lld g=%lld\n",i,j,f[i][j],g[i][j]);
	}
	//if(n==468) cout<<f[0][0]<<endl;
	//cout<<f[0][0]<<endl;
	if(f[0][0]<0) puts("Cry");
	else if(!f[0][0]) puts("Flowers");
	else puts("Break a heart");
	return 0;
}
/*
4 3
1 4
-1 -1 -1 -1
1 2 1
2 3 1
3 4 1
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值