1. 程式人生 > 其它 >【CF536D】Tavas in Kansas

【CF536D】Tavas in Kansas

題目

題目連結:https://codeforces.com/problemset/problem/536/D

  • 給定一張 \(n\) 個點 \(m\) 條邊的可能有自環和重邊的無向連通圖,每條邊都有一個非負邊權。
  • 小 X 和小 Y 在這張圖上玩一個遊戲,在遊戲中,第 \(i\) 個城市有一個權值 \(p_i\)
  • 一開始,小 X 在城市 \(s\) 中,小 Y 在城市 \(t\) 中,兩人各有一個得分,初始為 \(0\),小 X 為先手,然後輪流進行操作。
  • 當輪到某一個人時,他必須選擇一個非負整數 \(x\),以選定所有與他所在的城市的最短距離不超過 \(x\) 的還未被選定過的城市,他的得分將會加上這些城市的權值。
  • 另外,每個人每次必須能夠至少選定一個城市。
  • 當沒有人可以選擇時,遊戲結束,得分高者獲勝。
  • 現在請你計算出,在兩人都使用最佳策略的情況下,誰會獲勝(或者判斷為平局)。
  • \(n \le 2 \times 10^3\)\(m \le 10^5\)\(|p_i| \le 10^9\)

思路

首先跑兩次單元最短路徑,求出點 \(x\)\(s\)\(t\) 的距離 \(dis1_x,dis2_x\)
因為這道題只關心距離的大小關係,所以可以把 \(dis1,dis2\) 分別離散化一下。然後點 \(x\) 就對應到一個 \(n\times n\) 的網格的 \((dis1_x,dis2_x)\)

處。
那麼此時先手就是取頂部的若干行,後手就是取左側的若干列。
\(f[0/1][i][j]\) 表示現在到先手/後手取,先手取到第 \(i\) 行,後手取到第 \(j\) 列,此時先手權值減去後手權值的最大/最小值。
直接正著 dp 可能會把對方非最優的情況計算到自己最優的情況中(也就是對方不走這個方案,但是轉移的時候你的最優方案從對方這個不選的方案轉移過來),並且有兩種結束狀態。所以考慮倒過來 dp。
\(f[0][i][j]\) 為例,如果 \((i,j)\)\((i,n)\) 這個子矩陣內至少有一個點,那麼

\[f[0][i][j]=\max(f[0][i+1][j],f[1][i+1][j])+\text{calc}(i,j,i,n) \]

否則

\[f[0][i][j]=f[0][i+1][j] \]

最後只需要看 \(f[0][1][1]\) 的正負性即可。
時間複雜度 \(O(n^2)\)

程式碼

#include <bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;

const int N=2010,M=200010;
int n,m,s,t,tot,c1,c2,a[N],head[N],cnt[N][N];
ll sum[N][N],dis1[N],dis2[N],b[N],c[N],f[2][N][N];
bool vis[N];

struct edge
{
	int next,to,dis;
}e[M];

void add(int from,int to,int dis)
{
	e[++tot]=(edge){head[from],to,dis};
	head[from]=tot;
}

void dij(int s,ll *dis)
{
	memset(vis,0,sizeof(vis));
	priority_queue<pair<ll,int> > q;
	q.push(mp(0,s)); dis[s]=0;
	while (q.size())
	{
		int u=q.top().se; q.pop();
		if (vis[u]) continue;
		vis[u]=1;
		for (int i=head[u];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (dis[v]>dis[u]+e[i].dis)
			{
				dis[v]=dis[u]+e[i].dis;
				q.push(mp(-dis[v],v));
			}
		}
	}
}

int calc1(int x,int y,int xx,int yy)
{
	return cnt[xx][yy]-cnt[xx][y-1]-cnt[x-1][yy]+cnt[x-1][y-1];
}

ll calc2(int x,int y,int xx,int yy)
{
	return sum[xx][yy]-sum[xx][y-1]-sum[x-1][yy]+sum[x-1][y-1];
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=1,x,y,z;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); add(y,x,z);
	}
	memset(dis1,0x3f3f3f3f,sizeof(dis1));
	memset(dis2,0x3f3f3f3f,sizeof(dis2));
	dij(s,dis1); dij(t,dis2);
	for (int i=1;i<=n;i++)
		b[i]=dis1[i],c[i]=dis2[i];
	sort(b+1,b+1+n);
	c1=unique(b+1,b+1+n)-b-1;
	sort(c+1,c+1+n);
	c2=unique(c+1,c+1+n)-c-1;
	for (int i=1;i<=n;i++)
	{
		int x=lower_bound(b+1,b+1+c1,dis1[i])-b;
		int y=lower_bound(c+1,c+1+c2,dis2[i])-c;
		sum[x][y]+=a[i]; cnt[x][y]++;
	}
	for (int i=1;i<=c1+1;i++)
		for (int j=1;j<=c2+1;j++)
		{
			sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
			cnt[i][j]+=cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1];
		}
	for (int i=c1+1;i>=1;i--)
		for (int j=c2+1;j>=1;j--)
		{
			if (i==c1+1 && j==c2+1) continue;
			if (!calc1(i,j,i,c2)) f[0][i][j]=f[0][i+1][j];
				else f[0][i][j]=max(f[0][i+1][j],f[1][i+1][j])+calc2(i,j,i,c2);
			if (!calc1(i,j,c1,j)) f[1][i][j]=f[1][i][j+1];
				else f[1][i][j]=min(f[0][i][j+1],f[1][i][j+1])-calc2(i,j,c1,j);
		}
	if (!f[0][1][1]) cout<<"Flowers";
	if (f[0][1][1]>0) cout<<"Break a heart";
	if (f[0][1][1]<0) cout<<"Cry";
	return 0;
}