[JZOJ4684] 【GDOI2017模拟8.11】卡牌游戏
题目
描述
题目大意
有111到2n2n2n牌,一开始分别给两个人,每人nnn张。
轮流出牌,给出对手出牌的顺序,若自己的牌更大,就记一分。
在中间的某个时刻可以改变游戏规则。
问最大的分数。
思考历程
显然,一定是把大的放分界点左边,把小的放右边。
那可以枚举分界点,两边分别计算就可以了。时间复杂度为O(n2)O(n^2)O(n2),朴素的暴力算法。
接下来我就想有没有什么数据结构可以在枚举分界点的时候维护左右两边的答案。
想不出……
换种简单的思路。
当分界点从左向右移动的时候,左边的答案会增,右边的答案回减。
于是我就天真地想到了三分(如果画成函数图像就是一座山峰),打了个三分上去。
最终沮丧地发现三分是错的,不过还好,可以水到50分。
但实际上这并不是一座山峰,而是连绵起伏,没有什么规律……
莫名其妙地想到模拟退火,可不可以用它来AC这题?
正解
其实这题的解法比较多样,先说题解做法(我AC的做法):
枚举分界点,用数据结构维护左右两边的答案。
如何维护呢?
现在我们只考虑左边,右边的可以分开处理,方法是一模一样的。
我们现在有个大小为2n2n2n的桶,枚举到某个分界点时,这个桶里面有一些点有值,表示这个点存在。这个值还表示它是敌方还是我方。
接下来敌方点要分别在右边匹配我方点,使得匹配数最大。
然后就有一个比较粗暴的思路:可以考虑匹配最近的点。
用个线段树来维护,对于每个节点,记录当前区间内的我方点和敌方点的个数。
合并区间的时候,用左区间的敌方点配对右区间的我方点(尽量配对),加入答案,然后将剩余的加在一起。
这样就使得近的点先匹配到一起。
这个思路是正确的。不妨想想,对于每个敌方点,它们只能匹配在右边的我方点。显然从右到左,它们可以选择的集合的大小是递增的。既然要让匹配数最大,就应当尽量让左边的点在它的集合以内,并且右边的点的集合以外的,这样就不会影响右边的点的选择。所以匹配的最好方式是选择最近的点。
但合并的过程是从小区间到大区间,顺序是乱的,有没有可能出问题呢?出问题的原因在于两个点右边最近的点重合了。如果这样,就其中一个选点,然后另一个会再往后面找。我们不需要关心到底是哪个先选点,因为这样是等价的,我们没有必要关心这些。
有了这个数据结构之后扫一遍,记录答案就可以做出来了,时间复杂度O(nlgn)O(n\lg n)O(nlgn)
还有一个DYP发明的不同算法。主要思路是在桶中枚举,然后用数据结构来修改答案(把答案看成一个序列)。
先把左右两边分开计算(一下以左边为准)。开一个2n2n2n大小的桶。
根据贪心策略,从右到左,然后能匹配就匹配的方案一定是最优的。
从大到小在桶中扫,用一个变量来记录扫到的我方的数量。
遇见一个敌方的点,就开始搞事情:它可能会匹配右边的点。如果右边有点,那就可以匹配,它出现的时间那里的答案加一。如果右边的点,那就不可以匹配,就不加一。
我们维护的是整个答案序列。显然答案序列是递增的。所以可以在数据结构中二分出一个尽量后的地方,使得它的答案小于我方的数量,这就意味着在它后面时间中,那右边的点被占满了。所以就将它出现的时间和二分出来的时间形成的这段区间加一。
做完这些后就记录左边和右边的答案,然后合并。在之前我们可以记录当前的这个答案用到的最右(最左)的我方点位置,表示选取我方点的区间。合并的时候,如果区间重合,那鱼与熊掌不可得兼,减去重合部分就是真正的答案了。
代码
题解方法
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 50010
int n;
int a[N],b[N*2];
bool flag[N*2];
struct Node{
int p,d,w;
} d[N*8];
inline void update1(int k){
int newp=min(d[k<<1].w,d[k<<1|1].d);//两边的最大匹配数
d[k]={d[k<<1].p+d[k<<1|1].p+newp,d[k<<1].d+d[k<<1|1].d-newp,d[k<<1].w+d[k<<1|1].w-newp};
}
inline void add1(int k,int l,int r,int x){
if (l==r){
if (flag[x])
d[k]={0,0,1};
else
d[k]={0,1,0};
return;
}
int mid=l+r>>1;
if (x<=mid)
add1(k<<1,l,mid,x);
else
add1(k<<1|1,mid+1,r,x);
update1(k);
}
inline void update2(int k){
int newp=min(d[k<<1].d,d[k<<1|1].w);
d[k]={d[k<<1].p+d[k<<1|1].p+newp,d[k<<1].d+d[k<<1|1].d-newp,d[k<<1].w+d[k<<1|1].w-newp};
}
inline void add2(int k,int l,int r,int x){
if (l==r){
if (flag[x])
d[k]={0,0,1};
else
d[k]={0,1,0};
return;
}
int mid=l+r>>1;
if (x<=mid)
add2(k<<1,l,mid,x);
else
add2(k<<1|1,mid+1,r,x);
update2(k);
}
int ans1[N],ans2[N];
int main(){
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]),flag[a[i]]=1;
for (int i=1,j=0;i<=n*2;++i)
if (!flag[i])
b[++j]=i;
for (int i=0;i<n;++i){
ans1[i]=d[1].p;
add1(1,1,n<<1,a[i+1]);
add1(1,1,n<<1,b[n-i]);
}
ans1[n]=d[1].p;
memset(d,0,sizeof d);
for (int i=n;i>0;--i){
ans2[i]=d[1].p;
add2(1,1,n<<1,a[i]);
add2(1,1,n<<1,b[n-i+1]);
}
ans2[0]=d[1].p;
int ANS=0;
for (int i=0;i<=n;++i)
ANS=max(ANS,ans1[i]+ans2[i]);
printf("%d\n",ANS);
return 0;
}
总结
其实这是一个分治思路,只是用数据结构来动态实现罢了。
所以还是可以往分治方面想……
[JZOJ4684] 【GDOI2017模拟8.11】卡牌游戏的更多相关文章
- BZOJ_3191_[JLOI2013]卡牌游戏_概率DP
BZOJ_3191_[JLOI2013]卡牌游戏_概率DP Description N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随 ...
- 洛谷 P2059 [JLOI2013]卡牌游戏 解题报告
P2059 [JLOI2013]卡牌游戏 题意 有\(n\)个人玩约瑟夫游戏,有\(m\)张卡,每张卡上有一个正整数,每次庄家有放回的抽一张卡,干掉从庄家起顺时针的第\(k\)个人(计算庄家),干掉的 ...
- bzoj千题计划202:bzoj3191: [JLOI2013]卡牌游戏
http://www.lydsy.com/JudgeOnline/problem.php?id=3191 每个人获胜的概率只与其在排列中与庄家的相对位置有关 dp[i][j] 还剩i个人时,从庄家数第 ...
- 【BZOJ3191】【JLOI2013】卡牌游戏 [DP]
卡牌游戏 Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description N个人坐成一圈玩游戏.一开始我 ...
- TCG卡牌游戏研究:《炉石战记:魔兽英雄传》所做的改变
转自:http://www.gameres.com/665306.html TCG演进史 说到卡牌游戏,大家会联想到什么呢? 是历史悠久的扑克牌.风靡全球的<MTG 魔法风云会>与< ...
- [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计
源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...
- BZOJ 4392 卡牌游戏
Description 奶牛贝茜是卡牌游戏的狂热爱好者, 但是令人吃惊的, 她缺乏对手. 不幸的是, 任何牧 群里的其他牛都不是好对手. 他们实在是太差了 , 实际上, 他们玩卡牌游戏时会遵循一种完全 ...
- [JLOI2013]卡牌游戏 概率DP
[JLOI2013]卡牌游戏 概率DP 题面 \(dfs\)复杂度爆炸,考虑DP.发现决策时,我们只用关心当前玩家是从庄家数第几个玩家与当前抽到的牌是啥.于是设计状态\(f[i][j]\)表示有\(i ...
- [省选联考 2021 A/B 卷] 卡牌游戏
垃圾福建垫底选手来看看这题. 大家怎么都写带 \(log\) 的. 我来说一个线性做法好了. 那么我们考虑枚举 \(k\) 作为翻转完的最小值. 那么构造出一个满足条件的操作,我们在 \(a_i\) ...
- JLOI 2013 卡牌游戏
问题描述: N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随机(即按相等的概率)从卡牌堆里选择一张卡片,假设卡片上的数字为X,则庄家首先 ...
随机推荐
- ActiveMQ 反序列化漏洞(CVE-2015-5254)
java -jar jmet-0.1.0-all.jar -Q event -I ActiveMQ -s -Y "touch /tmp/success" -Yp ROME 192. ...
- 剑指offer——25合并两个排序的链表
题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 题解: 使用普通方法,或者递归,注意新的头节点即可. //使用普通的合并方法 class S ...
- adb shell 查看内存信息
1.根据包名来查看指定的APP指定数据adb shell "top | grep com.xxx.xxx" 由于这样打印出来的数据没有参数名,可以参考这个命令来看:adb shel ...
- 2019-7-29-C#-在基类定义好方法让子类继承接口就能实现
title author date CreateTime categories C# 在基类定义好方法让子类继承接口就能实现 lindexi 2019-07-29 09:57:49 +0800 201 ...
- python 模块间的引入
转载来自: https://www.cnblogs.com/whitemouseV2-0/p/9925344.html https://www.cnblogs.com/whitemouseV2-0/p ...
- Android开发 retrofit入门讲解
前言 retrofit基于okhttp封装的网络请求框架,网络请求的工作本质上是 OkHttp 完成,而 retrofit 仅负责网络请求接口的封装.如果你不了解OKhttp建议你还是先了解它在来学习 ...
- Linux命令速查手册(第2版)学习
第1章.需要了解的命令行相关事项 表1-1 如何在文件名字符中使用特殊字符 字符 建议 / 绝不使用.不能转义 \ 必须转义.避免使用 _ 绝不能作为文件或目录名的第一个字符 [] 必须转义.避免使用 ...
- LOIC Download
{ //https://github.com/NewEraCracker/LOIC }
- flink提交文件出现java.io.IOException:unable to close file because the last block does not have enough number of replicas异常
当提交已经打包好的jar包时候,控制台出现以下的错误.
- LUOGU P4159 [SCOI2009]迷路(矩阵乘法)
传送门 解题思路 以前bpw讲过的一道题,顺便复习一下矩阵乘法.做法就是拆点,把每个点拆成\(9\)个点,然后挨个连边.之后若\(i\)与\(j\)之间的边长度为\(x\),就让\(i\)的第\(x\ ...