洛谷题目页面传送门 & CodeForces题目页面传送门

A和B在一张无向连通图\(G=(V,E),|V|=n,|E|=m\)上玩一个游戏,节点\(i\)有一个权值\(v_i\)。A、B分别站在节点\(a,b\),轮流操作,每次操作选择一个数\(x\),然后选所有到自己所在地最短距离\(\le x\)且没有被选过的点,并把它们的权值累加到自己的分数中,且要满足至少选了\(1\)个点。直到当前玩家无法操作,游戏结束。A为先手,A、B均希望自己的得分更高且按最优策略操作。设A、B的最终得分分别为\(sco_a,sco_b\),则输出\(\begin{cases}\texttt{Cry}&sco_a<sco_b\\\texttt{Flowers}&sco_a=sco_b\\\texttt{Break a heart}&sco_a>sco_b\end{cases}\)。

\(n\in\left[2,2\times10^3\right],m\in\left[n-1,10^5\right],a\ne b,v_i\in\left[-10^9,10^9\right]\)。

首先,跑\(2\)遍Dijkstra(由于\(\mathrm O\!\left(n^2\right)\)可过,所以不需要堆优化)是肯定的,算出每个节点\(i\)到\(a,b\)分别的最短路径,记为\(dis1_i,dis2_i\)。

然后不难发现,以到\(a\)的最短距离来说,不可能存在\(dis1_i<dis1_j\)使得\(j\)被A选了而\(i\)没被选(道理很简单),到\(b\)亦然。那么我们可以很简单地描述出游戏中任意一个可能的局面:有且仅有所有满足\(dis1_x\le i\)或\(dis2_x\le j\)的\(x\)被选,记为\(S(i,j)\)。考虑将\(dis1,dis2\)离散化(因为我们只需要比较相对大小,且\(dis1,dis2\)的比较是互相独立的,所以不用保留原数值),这样显然\(\forall S(i,j),i,j\le n\),我们就可以把每个局面装到DP数组里,使得状态数在\(\mathrm O(n^2)\)级。

设\(dp1_{i,j},dp2_{i,j}\)分别表示若初始局面为局面\(S_{i,j}\),第一步该A、B操作,A得的分数。考虑枚举第一步该操作的玩家新选了那些节点(这些节点不能已经被另一个玩家选过),即将\(i/j\)增加到了多少,然后转移到下一个局面。状态转移方程:

\[\begin{cases}dp1_{i,j}=\max\limits_{\sum\limits_{o=i+1}^k\sum\limits_{dis1_p=o}[dis2_p>j]>0}\left\{dp2_{k,j}+\sum\limits_{o=i+1}^k\sum\limits_{dis1_p=o}[dis2_p>j]v_p\right\}\\dp2_{i,j}=\min\limits_{\sum\limits_{o=j+1}^k\sum\limits_{dis2_p=o}[dis1_p>i]>0}\left\{dp1_{i,k}\right\}\end{cases}
\]

这样\(1\)次转移显然是\(\mathrm O(n)\)的,总复杂度\(\mathrm O\!\left(n^3\right)\),会炸。注意到方程里面有区间和,于是预处理前缀和\(Cnt1_{i,j}=\sum\limits_{k=1}^i\sum\limits_{dis1_o=k}[dis2_o>j],Sum1_{i,j}=\sum\limits_{k=1}^i\sum\limits_{dis1_o=k}[dis2_o>j]v_o,Cnt2_{i,j}=\sum\limits_{k=1}^i\sum\limits_{dis2_o=k}[dis1_o>j]\),方程变为了:

\[\begin{cases}dp1_{i,j}=\max\limits_{Cnt1_{k,j}>Cnt1_{i,j}}\left\{dp2_{k,j}+Sum1_{k,j}-Sum1_{i,j}\right\}=\max\limits_{Cnt1_{k,j}>Cnt1_{i,j}}\left\{dp2_{k,j}+Sum1_{k,j}\right\}-Sum1_{i,j}\\dp2_{i,j}=\min\limits_{Cnt2_{i,k}>Cnt2_{i,j}}\left\{dp1_{i,k}\right\}\end{cases}
\]

首先明确一点,DP时是按\(i,j\)的倒序递推的(因为只有\(i'\ge i,j'\ge j\),\(S(i,j)\)才可能转移到\(S(i',j')\))。不难发现,\(dp1\)的转移方程中,决策变量\(k\)的取值范围一定是一个区间,设它为\([k_{\min},k_{\max}]\),则若\(j\)固定,随着\(i\)的下降,\(k_{\max}=|dis1|\)恒成立,\(k_{\min}\)单调不升。这样一来,就可以two-pointers了,我们可以维护\(now1,mx1\)数组,\(now1_o,mx1_o\)表示当\(j=o\)时当前的\(k_{\min}\)、所有决策中最大的\(dp2_{k,j}+Sum1_{k,j}\),\(i\)每减少\(1\)时,就将\(now1_j\)减少若干并更新\(mx1_j\)。对\(dp2\)的处理类似,维护\(now2,mn2\)。

最终比较\(dp1_{0,0}\)与\(\sum\limits_{i=1}^nv_i-dp1_{0,0}\)的大小即可。对了还有边界,就是无法转移到任何局面的DP值为\(0\)。

用了two-pointers,计算每个DP值的时间复杂度均摊\(\mathrm O(1)\),再加上最先的Dijkstra,总复杂度就是\(\mathrm O\!\left(n^2\right)\)了。

下面贴代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long//防爆int
#define mp make_pair
#define X first
#define Y second
#define pb push_back
const int inf=0x3f3f3f3f3f3f3f3f;
const int N=2000;
int n/*点数*/,m/*边数*/,a/*A所在节点*/,b/*B所在节点*/;
int v[N+1];//点权
vector<pair<int,int> > nei[N+1];//邻接表
int dis1[N+1],dis2[N+1];//到a、b的最短距离
bool vis[N+1];
void dijkstra(int x,int dis[]){//Dijkstra求dis1,dis2
for(int i=1;i<=n;i++)dis[i]=inf;
dis[x]=0;
memset(vis,0,sizeof(vis));
while(true){
pair<int,int> mn(inf,0);
for(int j=1;j<=n;j++)if(!vis[j])mn=min(mn,mp(dis[j],j));
int y=mn.Y;
if(!y)break;
vis[y]=true;
for(int j=0;j<nei[y].size();j++){
int z=nei[y][j].X,len=nei[y][j].Y;
dis[z]=min(dis[z],dis[y]+len);
}
}
// printf("dis=");for(int i=1;i<=n;i++)cout<<dis[i]<<" ";puts("");
}
vector<int> nums1,nums2;//离散化辅助数组
vector<int> hav1[N+1],hav2[N+1];//hav1[i]中存储所有满足dis1[x]=i(离散化之后)的节点x,hav2类似
void discrete(vector<int> &nums,int dis[],vector<int> hav[]){//离散化
nums.pb(-1);//比任何距离都小
for(int i=1;i<=n;i++)nums.pb(dis[i]);
sort(nums.begin(),nums.end());
nums.resize(unique(nums.begin(),nums.end())-nums.begin());
for(int i=1;i<=n;i++)hav[dis[i]=lower_bound(nums.begin(),nums.end(),dis[i])-nums.begin()].pb(i);
}
int Cnt1[N+1][N+1],Sum1[N+1][N+1],Cnt2[N+1][N+1];//前缀和
int now1[N+1],now2[N+1],mx1[N+1],mn2[N+1];//优化DP的two-pointers数组
int dp1[N+1][N+1],dp2[N+1][N+1];//DP数组
signed main(){
cin>>n>>m>>a>>b;
int sum=0;//所有点权和
for(int i=1;i<=n;i++)cin>>v[i],sum+=v[i];
while(m--){
int x,y,z;
cin>>x>>y>>z;
nei[x].pb(mp(y,z));nei[y].pb(mp(x,z));
}
dijkstra(a,dis1);dijkstra(b,dis2);//求出距离数组
discrete(nums1,dis1,hav1);discrete(nums2,dis2,hav2);//离散化距离数组
for(int i=1;i<nums1.size();i++)for(int j=0;j<nums2.size();j++){//预处理Cnt1,Sum1
Cnt1[i][j]=Cnt1[i-1][j];Sum1[i][j]=Sum1[i-1][j];
for(int k=0;k<hav1[i].size();k++){
bool ok=dis2[hav1[i][k]]>j;
Cnt1[i][j]+=ok;Sum1[i][j]+=ok*v[hav1[i][k]];
}
}
for(int i=1;i<nums2.size();i++)for(int j=0;j<nums1.size();j++){//预处理Cnt2
Cnt2[i][j]=Cnt2[i-1][j];
for(int k=0;k<hav2[i].size();k++)Cnt2[i][j]+=dis1[hav2[i][k]]>j;
}
for(int i=0;i<=n;i++)now1[i]=nums1.size(),now2[i]=nums2.size(),mx1[i]=-inf,mn2[i]=inf;//赋初始值
for(int i=nums1.size()-1;~i;i--)for(int j=nums2.size()-1;~j;j--){//按i,j倒序DP
while(now1[j]&&Cnt1[now1[j]-1][j]>Cnt1[i][j])now1[j]--,mx1[j]=max(mx1[j],dp2[now1[j]][j]+Sum1[now1[j]][j]);//two-pointers
dp1[i][j]=mx1[j]>-inf?/*能转移?*/mx1[j]-Sum1[i][j]/*按方程来*/:0/*为0*/;
while(now2[i]&&Cnt2[now2[i]-1][i]>Cnt2[j][i])now2[i]--,mn2[i]=min(mn2[i],dp1[i][now2[i]]);//同上2行
dp2[i][j]=mn2[i]<inf?mn2[i]:0;//同上2行
// printf("dp1[%lld][%lld]=%lld dp2[%lld][%lld]=%lld\n",i,j,dp1[i][j],i,j,dp2[i][j]);
}
int ans=dp1[0][0];//A的最优得分
puts(ans<sum-ans?"Cry":ans==sum-ans?"Flowers":"Break a heart");
return 0;
}

CodeForces 536D Tavas in Kansas的更多相关文章

  1. Codeforces 536D - Tavas in Kansas(dp)

    Codeforces 题目传送门 & 洛谷题目传送门 其实这题本该 2019 年 12 月就 AC 的(详情请见 ycx 发此题题解的时间),然鹅鸽到了现在-- 首先以 \(s,t\) 分别为 ...

  2. [CF536D]Tavas in Kansas

    [CF536D]Tavas in Kansas 题目大意: 一张\(n(n\le2000)\)个点,\(m(m\le10^5)\)条边的无向带权连通图(权值可以为负).A.B两人分别在\(s,t\)点 ...

  3. Codeforces 535D - Tavas and Malekas

    535D - Tavas and Malekas 题目大意:给你一个模板串,给你一个 s 串的长度,告诉你 s 串中有 m 个模板串并告诉你,他们的其实位置, 问你这样的 s 串总数的多少,答案对1e ...

  4. Codeforces 535C - Tavas and Karafs

    535C - Tavas and Karafs 思路:对于满足条件的r,max(hl ,hl+1 ,hl+2 ,......,hr )<=t(也就是hr<=t)且∑hi<=t*m.所 ...

  5. Codeforces B - Tavas and SaDDas

    535B - Tavas and SaDDas 方法一:打表大法. 代码1: #include<bits/stdc++.h> using namespace std; ]={,,,,,,, ...

  6. Codeforces 535B Tavas and SaDDas 水题一枚

    题目链接:Tavas and SaDDas Once again Tavas started eating coffee mix without water! Keione told him that ...

  7. Codeforces 536C Tavas and Pashmaks(凸壳)

    题目链接 Tavas and Pashmaks 题目大意:n个人比赛,游泳和赛跑,游泳距离S,赛跑R.每个人对应两个速度(陆地和水上的),如果存在S,R,使得第i个人胜利,那么输出i 题目要求输出所有 ...

  8. codeforces 535D. Tavas and Malekas KMP

    题目链接 又复习了一遍kmp....之前都忘光了 #include<bits/stdc++.h> using namespace std; #define pb(x) push_back( ...

  9. codeforces 536a//Tavas and Karafs// Codeforces Round #299(Div. 1)

    题意:一个等差数列,首项为a,公差为b,无限长.操作cz是区间里选择最多m个不同的非0元素减1,最多操作t次,现给出区间左端ll,在t次操作能使区间全为0的情况下,问右端最大为多少. 这么一个简单题吞 ...

随机推荐

  1. asp.net Server.Transfer

    页面跳转传参. 如果不是通用的跳转可以通过,在原始页面定义对象保存数据 跳转的目标页面可以: SourcePage page=(SourcePage)Context.Handler; //获取源页面的 ...

  2. dapi 基于Django的轻量级测试平台七 怎样部署到生产环境

    QQ群: GitHub:https://github.com/yjlch1016/dapi Nginx+uWSGI 前置条件:以下所有操作均在root账号下面进行如果不是root用户请注意权限问题因为 ...

  3. 最常见Linux操作

    命令 含义 cd /home/hadoop #把/home/hadoop设置为当前目录 cd .. #返回上一级目录 cd ~ #进入到当前Linux系统登录用户的主目录(或主文件夹).在 Linux ...

  4. Spring Boot 注入外部配置到应用内部

    Spring Boot允许你外部化你的配置,这样你就可以在不同的环境中使用相同的应用程序代码,你可以使用properties文件.YAML文件.环境变量和命令行参数来外部化配置,属性值可以通过使用@V ...

  5. centos7删除Apache组件

    非特殊需要不要删除centos7中Apache等组件!首先查看centos中Apache版本(前面我们说了centos7删除PHP,centos7删除MariaDB,可能很多朋友会有疑问为什么要把所有 ...

  6. 使用Git管理品优购项目 开始部分

  7. JavaScript词法作用域—你不知道的JavaScript上卷读书笔记(一)

    前段时间在每天往返的地铁上抽空将 <你不知道的JavaScript(上卷)>读了一遍,这本书很多部分写的很是精妙,对于接触前端时间不太久的人来说,就好像是叩开了JavaScript的另一扇 ...

  8. ESA2GJK1DH1K基础篇: Android实现SmartConfig简单Demo

    下载源码去 百度安信可 导入源码 等待加载完 我的提示更新下软件 ,我就更新下 安装完成以后重新导入工程 安装到手机 注意,由于Android 9.0 以后的获取WIFI名称需要打开GPS,所以如果提 ...

  9. iptables 常用处理动作

    在iptables中,-j 参数用来指定要进行的处理动作,常用的处理动作包括:ACCEPT.REJECT.DROP.REDIRECT.MASQUERADE.LOG.DNAT.SNAT.MIRROR.Q ...

  10. 修改ARP缓存表大小

    在下发Mininet的ARP缓存表表项时,出现了如下的错误信息: SIOCSARP: No buffer space available 这是由于ARP表是缓存在内存中的,超过了系统对ARP缓存表大小 ...