Codeforces 536D - Tavas in Kansas(dp)

Codeforces 题目传送门 & 洛谷题目传送门

其实这题本该 2019 年 12 月就 AC 的(详情请见 ycx 发此题题解的时间),然鹅鸽到了现在……

首先以 \(s,t\) 分别为源点跑两遍 dijkstra 是不可避免的,求出 \(s,t\) 到每个点的最短距离 \(d1_i,d2_i\)

其次还可以发现一件事情,那就是对于 \(\forall i,j\),若 \(d1_i<d1_j\) 并且 \(j\) 被选择了,那么 \(i\) 一定被选择了,对于 \(d2_i\) 也可得到类似的性质。换句话说,对于游戏任意一种局面,\(\exist x,y\) 使得恰好只剩下 \(d1_i\geq x\)\(d2_i\geq y\)\(i\) 没有被选择。故考虑以此为状态设计 dp

\(d1,d2\) 离散化。设 \(dp_{p,x,y}\) 表示此时轮到 \(p\)\(0\) 表示先手,\(1\) 表示后手),还剩下 \(d1_i\geq x\)\(d2_i\geq y\) 的点没有被选择,所能得到的最大得分。状态转移方程很容易得到,以 \(p=0\) 为例,显然这一步操作之后留下来的应当是 \(d1_i\geq z\)\(d2_i\geq y\) 的城市,其中 \(z\) 为某个大于 \(x\) 的数,我们不妨枚举 \(z\),记 \(c_{x,y}\)\(d1_i\geq x\)\(d2_i\ge y\) 的城市个数,\(s_{x,y}\)\(d1_i\geq x\)\(d2_i\ge y\) 的城市的权值之和,由于先手要多占领一些城市,故 \(c_{z,y}>c_{x,y}\),故我们有状态转移方程 \(dp_{0,x,y}=s_{x,y}-\min\limits_{c_{z,y}>c_{x,y}}dp_{1,z,y}\)\(dp_{1,x,y}\) 也同理。

容易发现对于某个 \(dp_{p,x,y}\),其决策的集合是一段后缀,故可以预处理 \(nt1_{i,j}\) 表示第一个满足 \(c_{k,j}>c_{i,j}\)\(k\)\(nt2_{i,j}\) 表示 \(c_{i,l}>c_{i,j}\)\(l\),这个预处理复杂度显然是 \(n^2\) 的,处理完之后就可以利用后缀 \(\min\) 进行 \(\mathcal O(1)\) 转移了。

最终先手得分的最大值即为 \(dp_{0,0,0}\),后手得分即为 \(s_{0,0}-dp_{0,0,0}\),比较二者大小即可。

时间复杂度 \(n^2\)。总之此题算 D1D 中较简单的了。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=2e3;
const int MAXM=1e5;
int n,m,s,t,p[MAXN+5],to[MAXM*2+5],nxt[MAXM*2+5],cst[MAXM*2+5],hd[MAXN+5],ec=0;
void adde(int u,int v,int w){to[++ec]=v;cst[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
ll d1[MAXN+5],d2[MAXN+5],key1[MAXN+5],key2[MAXN+5],uni1[MAXN+5],uni2[MAXN+5];
int c1[MAXN+5],c2[MAXN+5],num1=0,num2=0,cnt[MAXN+5][MAXN+5];ll sum[MAXN+5][MAXN+5];
ll dp[2][MAXN+5][MAXN+5],mn[2][MAXN+5][MAXN+5];
int nt1[MAXN+5][MAXN+5],nt2[MAXN+5][MAXN+5];
int main(){
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=n;i++) scanf("%d",&p[i]);
	for(int i=1,u,v,w;i<=m;i++){scanf("%d%d%d",&u,&v,&w);adde(u,v,w);adde(v,u,w);}
	priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
	memset(d1,63,sizeof(d1));d1[s]=0;q.push(mp(0,s));
	while(!q.empty()){
		pii pr=q.top();q.pop();int x=pr.se;
		for(int e=hd[x];e;e=nxt[e]){
			int y=to[e],z=cst[e];
			if(d1[y]>d1[x]+z){
				d1[y]=d1[x]+z;
				q.push(mp(d1[y],y));
			}
		}
	}
	memset(d2,63,sizeof(d2));d2[t]=0;q.push(mp(0,t));
	while(!q.empty()){
		pii pr=q.top();q.pop();int x=pr.se;
		for(int e=hd[x];e;e=nxt[e]){
			int y=to[e],z=cst[e];
			if(d2[y]>d2[x]+z){
				d2[y]=d2[x]+z;
				q.push(mp(d2[y],y));
			}
		}
	}
	for(int i=1;i<=n;i++) key1[i]=d1[i],key2[i]=d2[i];
	sort(key1+1,key1+n+1);sort(key2+1,key2+n+1);key1[0]=key2[0]=-1;
	for(int i=1;i<=n;i++){
		if(key1[i]!=key1[i-1]) uni1[++num1]=key1[i];
		if(key2[i]!=key2[i-1]) uni2[++num2]=key2[i];
	}
	for(int i=1;i<=n;i++) c1[i]=lower_bound(uni1+1,uni1+num1+1,d1[i])-uni1;
	for(int i=1;i<=n;i++) c2[i]=lower_bound(uni2+1,uni2+num2+1,d2[i])-uni2;
//	for(int i=1;i<=n;i++) printf("%d %d\n",c1[i],c2[i]);
	for(int i=1;i<=n;i++) cnt[c1[i]][c2[i]]++,sum[c1[i]][c2[i]]+=p[i];
	for(int i=num1;i;i--) for(int j=num2;j;j--){
		cnt[i][j]+=cnt[i+1][j]+cnt[i][j+1]-cnt[i+1][j+1];
		sum[i][j]+=sum[i+1][j]+sum[i][j+1]-sum[i+1][j+1];
	}
//	for(int i=1;i<=num1;i++) for(int j=1;j<=num2;j++)
//		printf("%d %d %lld %lld\n",i,j,cnt[i][j],sum[i][j]);
	for(int i=num1;i;i--) for(int j=num2;j;j--){
		nt1[i][j]=(cnt[i][j]==cnt[i+1][j])?nt1[i+1][j]:(i+1);
		nt2[i][j]=(cnt[i][j]==cnt[i][j+1])?nt2[i][j+1]:(j+1);
	}
	for(int i=num1;i;i--) for(int j=num2;j;j--){
		if(!cnt[i][j]) continue;
		dp[0][i][j]=sum[i][j]-mn[1][nt1[i][j]][j];
		dp[1][i][j]=sum[i][j]-mn[0][i][nt2[i][j]];
//		printf("%d %d %d %d\n",i,j,dp[0][i][j],dp[1][i][j]);
		mn[0][i][j]=min(mn[0][i][j+1],dp[0][i][j]);
		mn[1][i][j]=min(mn[1][i+1][j],dp[1][i][j]);
	} ll sgn=sum[1][1]-(dp[0][1][1]<<1);
//	printf("%d\n",dp[0][1][1]);
	printf("%s\n",(sgn>0)?"Cry":((!sgn)?"Flowers":"Break a heart"));
	return 0;
}
posted @ 2021-02-26 23:09  tzc_wk  阅读(57)  评论(0)    收藏  举报