题目

描述

题目大意

有111到2n2n2n牌,一开始分别给两个人,每人nnn张。

轮流出牌,给出对手出牌的顺序,若自己的牌更大,就记一分。

在中间的某个时刻可以改变游戏规则。

问最大的分数。


思考历程

显然,一定是把大的放分界点左边,把小的放右边。

那可以枚举分界点,两边分别计算就可以了。时间复杂度为O(n2)O(n^2)O(n2),朴素的暴力算法。

接下来我就想有没有什么数据结构可以在枚举分界点的时候维护左右两边的答案。

想不出……

换种简单的思路。

当分界点从左向右移动的时候,左边的答案会增,右边的答案回减。

于是我就天真地想到了三分(如果画成函数图像就是一座山峰),打了个三分上去。

最终沮丧地发现三分是错的,不过还好,可以水到50分。

但实际上这并不是一座山峰,而是连绵起伏,没有什么规律……

莫名其妙地想到模拟退火,可不可以用它来AC这题?


正解

其实这题的解法比较多样,先说题解做法(我AC的做法):

枚举分界点,用数据结构维护左右两边的答案。

如何维护呢?

现在我们只考虑左边,右边的可以分开处理,方法是一模一样的。

我们现在有个大小为2n2n2n的桶,枚举到某个分界点时,这个桶里面有一些点有值,表示这个点存在。这个值还表示它是敌方还是我方。

接下来敌方点要分别在右边匹配我方点,使得匹配数最大。

然后就有一个比较粗暴的思路:可以考虑匹配最近的点。

用个线段树来维护,对于每个节点,记录当前区间内的我方点和敌方点的个数。

合并区间的时候,用左区间的敌方点配对右区间的我方点(尽量配对),加入答案,然后将剩余的加在一起。

这样就使得近的点先匹配到一起。

这个思路是正确的。不妨想想,对于每个敌方点,它们只能匹配在右边的我方点。显然从右到左,它们可以选择的集合的大小是递增的。既然要让匹配数最大,就应当尽量让左边的点在它的集合以内,并且右边的点的集合以外的,这样就不会影响右边的点的选择。所以匹配的最好方式是选择最近的点。

但合并的过程是从小区间到大区间,顺序是乱的,有没有可能出问题呢?出问题的原因在于两个点右边最近的点重合了。如果这样,就其中一个选点,然后另一个会再往后面找。我们不需要关心到底是哪个先选点,因为这样是等价的,我们没有必要关心这些。

有了这个数据结构之后扫一遍,记录答案就可以做出来了,时间复杂度O(nlg⁡n)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】卡牌游戏的更多相关文章

  1. BZOJ_3191_[JLOI2013]卡牌游戏_概率DP

    BZOJ_3191_[JLOI2013]卡牌游戏_概率DP Description   N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随 ...

  2. 洛谷 P2059 [JLOI2013]卡牌游戏 解题报告

    P2059 [JLOI2013]卡牌游戏 题意 有\(n\)个人玩约瑟夫游戏,有\(m\)张卡,每张卡上有一个正整数,每次庄家有放回的抽一张卡,干掉从庄家起顺时针的第\(k\)个人(计算庄家),干掉的 ...

  3. bzoj千题计划202:bzoj3191: [JLOI2013]卡牌游戏

    http://www.lydsy.com/JudgeOnline/problem.php?id=3191 每个人获胜的概率只与其在排列中与庄家的相对位置有关 dp[i][j] 还剩i个人时,从庄家数第 ...

  4. 【BZOJ3191】【JLOI2013】卡牌游戏 [DP]

    卡牌游戏 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description   N个人坐成一圈玩游戏.一开始我 ...

  5. TCG卡牌游戏研究:《炉石战记:魔兽英雄传》所做的改变

    转自:http://www.gameres.com/665306.html TCG演进史 说到卡牌游戏,大家会联想到什么呢? 是历史悠久的扑克牌.风靡全球的<MTG 魔法风云会>与< ...

  6. [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计

    源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...

  7. BZOJ 4392 卡牌游戏

    Description 奶牛贝茜是卡牌游戏的狂热爱好者, 但是令人吃惊的, 她缺乏对手. 不幸的是, 任何牧 群里的其他牛都不是好对手. 他们实在是太差了 , 实际上, 他们玩卡牌游戏时会遵循一种完全 ...

  8. [JLOI2013]卡牌游戏 概率DP

    [JLOI2013]卡牌游戏 概率DP 题面 \(dfs\)复杂度爆炸,考虑DP.发现决策时,我们只用关心当前玩家是从庄家数第几个玩家与当前抽到的牌是啥.于是设计状态\(f[i][j]\)表示有\(i ...

  9. [省选联考 2021 A/B 卷] 卡牌游戏

    垃圾福建垫底选手来看看这题. 大家怎么都写带 \(log\) 的. 我来说一个线性做法好了. 那么我们考虑枚举 \(k\) 作为翻转完的最小值. 那么构造出一个满足条件的操作,我们在 \(a_i\) ...

  10. JLOI 2013 卡牌游戏

    问题描述: N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随机(即按相等的概率)从卡牌堆里选择一张卡片,假设卡片上的数字为X,则庄家首先 ...

随机推荐

  1. 微信-小程序-开发文档-服务端-模板消息:templateMessage.addTemplate

    ylbtech-微信-小程序-开发文档-服务端-模板消息:templateMessage.addTemplate 1.返回顶部 1. templateMessage.addTemplate 本接口应在 ...

  2. debian 源设置 ( apt-get 不能安装)

    使用说明 以Jessie为例, 编辑/etc/apt/sources.list文件, 在文件最前面添加以下条目(操作前请做好相应备份) deb http://mirrors.163.com/debia ...

  3. 『BASH』——Learn BashScript from Daniel Robbins——[003]

    ABSTRACT: Daniel Robbins is best known as the creator of Gentoo Linux and author of many IBM develop ...

  4. CentOS增加swap分区大小

    来自:http://www.centoscn.com/CentOS/Intermediate/2014/0222/2446.html 1. 查看当前分区情况 free -m 2. 增加 swap 大小 ...

  5. Selenium(二)---无界面模式+滑动底部

    一.使用无界面模式 1.正常情况启动 selenium 是有界面的 2.有些情况下,需要不显示界面,这时只要设置一下参数就可以实现了 # 不想显示界面可以用 Chrome——配置一下参数就好 from ...

  6. OpenLiveWriter博客工具

    1.OpenLiveWriter安装 官网下载地址:http://openlivewriter.org/ 默认安装到:C:\Users\用户\AppData\Local\OpenLiveWriter目 ...

  7. iOS开发系列-Foundation与CoreFoundation内存管理

    概述 对于初学者来说,可能仅只能将ARC用在objective-c对象上(也即继承自NSObject的对象),但是如果涉及到较为底层的东西,比如Core Foundation中的malloc()或者f ...

  8. C#利用资源文件设置软件自适应多语言

    在项目更目录下添加两个资源文件,以适应中英文两种版本,如Resource.zh_CN.resx和      Resource.en-US.resx  ,两个资源文件的ID都一样,值分别配置相应的中英文 ...

  9. 转: https原理:证书传递、验证和数据加密、解密过程解析

    原本连接:http://www.cnblogs.com/zhuqil/archive/2012/07/23/2604572.html 我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取.所以很 ...

  10. 解决Keep-Alive 和 Close 不能使用此属性设置

    http://www.hejingzong.cn/blog/viewblog_86.aspx Keep-Alive 和 Close 不能使用此属性设置 public static void SetHe ...