HGOI 20191105 题解
Problem A Joker
老虎和蒜头是好朋友。
夏天过去了,凉爽的秋天来临,老虎和蒜头又有了新的娱乐项目。
老虎有一个远房表亲是西伯利亚虎,那里流行着一个纸牌游戏:两位玩家参与游戏,道具是一副54张的扑克牌,52 张基本牌和两张Joker,也称为王牌。这两张王分别是红色和黑色的。这52 张基本牌又分为黑桃、红桃、梅花、方片四种花色,每种花色均有13 张牌,这13 张牌会有一个等级顺序,依次分别是2 3 4 5 6 7 8 9 10 J Q K A,其中2 是最低的等级,A 是最高的等级。所有的红桃和方片都被认为是红色的,而所有的黑桃和梅花都被认为是黑色的。有一种花色会被硬点,我们称这种花色的牌为庄家牌。
一局游戏开始前,两名玩家手上都有六张牌,剩余的42 张牌组成牌堆。一局游戏由多轮游戏组成,在每一轮开始前,两名玩家手上均有若干张牌,一名玩家先手,另一名玩家后手。先手的玩家应在桌上放若干张等级相同的牌,注意先手玩家在任意时刻都不允许放置王牌,且放置的牌的张数不能超过后手玩家手上拥有牌的总数。后手玩家可以选择一些自己的牌分别叠在先手玩家的牌上,一张牌可以叠在另一张牌上当且仅当以下条件中至少有一条满足:
1. 这张牌和被叠的牌花色相同,且有着更高的等级。
2. 这张牌为庄家牌,而被叠的牌不是。
3. 这张牌是王牌,且和被叠的牌颜色相同。
4. 这张牌是王牌,并且它的颜色和为庄家牌的颜色相同。在所有桌上的牌都被叠后,先手者还可以在桌面上再放置若干张牌,但这些牌的等级一定要和桌上的任意一张牌的等级相同(无论是否被叠),而后手的玩家仍然可以选择一些自己的牌叠在先手玩家的牌上。注意,先手玩家仍然不能在桌上放置比后手玩家的手牌数更多的牌。
一轮游戏结束,当且仅当以下两种情况之一发生:后手玩家不能或不想叠桌面上的牌、或是先手玩家不能或不想放置更多的牌。在第一种情况下,当后手玩家认为不必继续这一轮游戏时,先手玩家仍然有一次继续放置非王牌的扑克牌的机会,而放置的牌的等级仍要和桌面上已有的任一张牌等级相同,且桌面上未被叠的牌的总数不能超过后手玩家手上牌的总数。在此之后,后手玩家输掉了这一轮,并将桌面上的所有牌加入自己的手牌。在下一轮游戏中,先手玩家和后手玩家的角色不变。
在第二种情况下,后手玩家赢得了这一轮。桌面上的所有牌将被移走(并不移入牌堆),而在下一轮中先手和后手玩家的角色将会互换,即先手玩家变为后手玩家,后手玩家变为先手玩家。
在轮与轮之间,如果上一轮的先手玩家的手牌不超过6 张,他会从牌堆顶部开始一张一张取入手牌,直到手牌恰好有6 张或是牌堆中所有牌均已被取走。
如果在一轮游戏开始前,一名玩家已经没有手牌,而另一位玩家仍有手牌,那么无手牌的玩家赢得了这一局游戏。如果两位玩家都没有了手牌,那么本局游戏以平局收场。在两名玩家都有至少一张手牌,但先手玩家的手牌只有王牌——他不能做任何行动——的情况下,我们认为后手玩家取得本局游戏的胜利。
老虎和蒜头正在进行这样的游戏,我们给定牌堆的情况、两人手中的手牌以及特殊牌的花色,你的目标是确定在最优策略下这局游戏的胜者是谁。注意:牌堆的信息对两人来说都是公开的。西伯利亚虎是老虎的朋友,因此蒜头决定每局游戏中让老虎先手。
老虎认为这个游戏太复杂了,因此在每一局游戏进行前老虎会先从所有牌中去掉某些等级的牌,但老虎保证至少剩下3 个等级的牌,即至少有14 张牌以使得游戏能顺利进行。
Solution :
还好本题在知乎上看到过原题,这是tourist出的神仙题。
无用信息对于$95\%$,个人认为本题没有什么价值。
只要后手没有两张王,那么就后手必胜,否则先手必胜。
最优策略下,后手只需要一直不出然后让先手将牌堆摸遍就好了。
是一个Trick题
#include <bits/stdc++.h> using namespace std; const int N = ; string s1, s2, s3, c, level; int main() {
int T; cin >> T;
char ch; cin >> ch;
while (T--) {
int flg = ;
getline(cin, level);
getline(cin, s1);
getline(cin, s2);
for (int i = ; i < (int) s2.size(); i++) {
if (s2[i] == 'R' && s2[i + ] == 'J')
flg++;
if (s2[i] == 'B' && s2[i + ] == 'J')
flg++;
}
getline(cin, s3);
getline(cin, c);
if (flg == )
printf("laohu\n");
else
printf("dasuan\n");
}
return ;
}
joker.cpp
Problem B swap
给出两个长度为$n$的排列$a,b$,每一次操作可以将$a$中的一段$[L,R]$拿出,将其最大值和最小值交换顺序。
在$345678$次操作之内,将$a$变成$b$.
对于$100\%$的数据保证,$1 \leq n \leq 4096$
Solution :
之前有出过类似的题目,仍然考虑归并排序套快速排序的思想。
如果我们可以将一个$a$数组排序,其操作序列为$S$,将$b$数组排序,其操作序列为$T$
那么由于操作可逆,所以我们只需要正序输出$S$,然后倒序输出$T$即可。
利用归并排序,问题可以转化为,如何合并两个递增有序的序列。
这个事情,我们需要使用一个分治做法来完成。
设当前需要合并的区间为$[l0,r0]$和$[l1,r1]$
设要合并的线段是$[l0,r0]$,$[l1,r1]$,找到一个最小的长度$pt$,使得$[r0-pt+1,r0]$的数的大小都比$[l1,l1+pt-1]$的数的大小大。
这个时候,我们需要将$[r0-pt+1,l1+pt-1]$区间的数有序,所以我们需要依次做三个$update$操作。
即翻转区间$[r0,pt+1,r0]$,翻转区间$[l1,l1+pt-1]$,翻转区间$[r0-pt+1,l1+pt-1]$
此时问题就转化为两个子问题: 将$[l0,l0-pt]$和$[r0-pt+1,r0]$合并; 将$[l1,l1+pt-1]$和$[l1+pt,r1]$合并。
这样的分治,每一次将会将线段均分成两段,时间复杂度为$O(log_2 l)$,其中$l$表示处理线段长度。
所以,外套一个归并排序,本题的最终时间复杂度为$O(n {log_2}^2 n)$
# include<bits/stdc++.h>
using namespace std;
const int N=1e5+;
int n,a[N];
vector< pair<int,int> >ans[];
void update(int op,int l,int r) {
for (int i=;i<(r-l+)/;i++) {
ans[op].push_back(make_pair(l+i,r-i)),swap(a[l+i],a[r-i]);
}
}
void merge(int op,int l0,int r0,int l1,int r1) {
if (l0>r0 || l1>r1) return;
int l=min(r0-l0,r1-l1),pt=;
while (pt<=l) if (a[r0-pt]>=a[l1+pt]) pt++; else break;
if (!pt) return;
update(op,r0-pt+,r0);
update(op,l1,l1+pt-);
update(op,r0-pt+,l1+pt-);
merge(op,l0,r0-pt,r0-pt+,r0);
merge(op,l1,l1+pt-,l1+pt,r1);
}
void solve(int op,int l,int r) {
if (l >= r) return;
int mid = (l+r)>>;
solve(op,l,mid); solve(op,mid+,r);
merge(op,l,mid,mid+,r);
}
int main()
{
scanf("%d",&n);
for (int i=;i<=n;i++) scanf("%d",&a[i]);
solve(,,n);
for (int i=;i<=n;i++) scanf("%d",&a[i]);
solve(,,n);
reverse(ans[].begin(),ans[].end());
printf("%d\n",ans[].size()+ans[].size());
for (int i=;i<ans[].size();i++) printf("%d %d\n",ans[][i].first,ans[][i].second);
for (int i=;i<ans[].size();i++) printf("%d %d\n",ans[][i].first,ans[][i].second);
return ;
}
swap.cpp
Problem C actor
你有$m$种颜料,你要按照某个顺序选择颜料,每次在长度为$n$
的墙壁上刷一个区间,墙壁必须刷满,求可能刷出的墙壁方案数。
对于$100\%$的数据满足$1 \leq n\leq 10^6, m \leq 300$
Solution :
我们会首先考虑一个$O(mn^2)$的暴力$dp$.
设$f[i][j][k]$表示当前涂到第$i$个格子,使用了$j$个颜色,而有$k$个颜色一定会在后来的涂色中涂到。
那么$f[0][0][0] = 1$,采用刷表法转移。
考虑$f[i][j][k]$可以转移到
新放一个在之后将不会再刷到这个颜色: $f[i+1][j+1][k]$
新放一个在之后还会刷到这个颜色:$f[i+1][j+1][k+1]$
选择一个老颜色放在当前位置,并终结$f[i+1][j][k-1]$,此时要求$k\geq 1$
选择一个老颜色放在当前位置,并不终结$f[i+1][j][k]$, 此时要求$k\geq 1$
答案就是$\sum\limits_{i = 1}^{m} ({A_m}^i \times f[n][i][0])$
我们考虑到,最终的答案必然是由最多$2m$个相同颜色的块组成的,所以第一维状态可以压成和$m$有关系的了。
如果求出了块数为$i$的方案数$s$,那么可以通过插板法求出原来序列的答案了,即${C_{n-1}}^{i-1} \times s$
将状态改为$f[i][j][k]$表示当前考虑到$i$块,已经使用$j$个颜色,该块和该块之后最多能放置的已经放过的颜色的数目为$k$。
那么,$f[i+1][j][k] = f[i][j][k+1]+f[i][j][k+2] + ...$ ,此时只要在第$i+1$放一个已经放过的颜色就能实现。
若考虑新放置一个$j+1$的颜色,其有$(m-j)$种元素好挑,所以转移方程为$f[i+1][j+1][k+1] += f[i][j][k] \times (m-j)$
答案按照上述方法统计即可。
时间复杂度为$O(m^3)$
#include <stdio.h>
typedef unsigned long long u64;
const int N = , P = 1e9 + ;
int n, m, f[N << ][N][N], c[N << ], inv[N << ], sum;
void inc(int &x, int y){
(x += y) < P ?: x -= P;
}
void inc(int &x, u64 y){
x = (x + y) % P;
}
int main(){
// freopen("actor.in", "r", stdin);
// freopen("actor.out", "w", stdout);
int i, j, k, s;
scanf("%d%d", &n, &m);
f[][][] = m, c[] = inv[] = ;
for (i = ; i <= m << ; ++i) {
inv[i] = P - (u64)(P / i) * inv[P % i] % P;
c[i] = (u64)c[i - ] * inv[i - ] % P * (n - i + ) % P;
}
for (i = ; i <= m << ; ++i) {
for (j = ; j <= m; ++j) {
for (k = j, s = ; k >= ; --k) {
inc(f[i + ][j][k], s);
inc(f[i + ][j + ][k + ], (u64)f[i][j][k] * (m - j));
inc(s, f[i][j][k]);
}
inc(sum, (u64)s * c[i]);
}
}
printf("%d\n", sum);
}
Actor.cpp
HGOI 20191105 题解的更多相关文章
- HGOI 20181028 题解
HGOI 20181028(复赛备考) /* 真是暴力的一天,最后一题MLE?由于数组开得太大了!!! 270滚粗 考场上好像智商高了很多?!(假的) */ sol:暴力求解,然后没有数据范围吐槽一下 ...
- HGOI 20190310 题解
/* 又是又双叒叕WA的一天... 我太弱鸡了... 今天上午打了4道CF */ Problem 1 meaning 给出q组询问,求下列函数的值$ f(a) = \max\limits_{0 < ...
- HGOI 20190303 题解
/* 记一串数字真难. 5435 今天比赛又是hjcAK的一天. 今天开题顺序是312,在搞T1之前搞了T3 昨天某谷月赛真是毒瘤. 但是讲评的同学不错,起码T4看懂了... 构造最优状态然后DP的思 ...
- HGOI 20180224 题解
/* The Most Important Things: ljc chat with fyh on QQTa说期末考Ta数学74分感觉不好但是我觉得fyh是地表最强的鸭~~(of course en ...
- HGOI 20190218 题解
/* 又是AK局... hjc又双叒叕AK了... Hmmm...我侥幸 */ Problem A card 给出无序序列a[]可以选择一个数插入到合适的位置作为一次操作,至少多少次操作后可以把序列变 ...
- HGOI 20190217 题解
/* for me,开训第一天 /beacuse 文化课太差被抓去补文化课了... 看一眼题 : AK局? 但是,Wa on test #10 in problem C 290! (就差那么一咪咪) ...
- HGOI 20181103 题解
problem:把一个可重集分成两个互异的不为空集合,两个集合里面的数相乘的gcd为1(将集合中所有元素的质因数没有交集) solution:显然本题并不是那么容易啊!考场上想了好久.. 其实转化为上 ...
- HGOI 20181101题解
/* 又是爆0的一天(不知道今年高考难不难,反正今天(信息学)真的难!) */ solution:对于两个数相加,有一个显然的结论就是要么不进位(相对于位数大的),要么(进最多一位) 然后对于整个数组 ...
- HGOI 20191108 题解
Problem A 新婚快乐 一条路,被$n$个红绿灯划分成$n+1$段,从前到后一次给出每一段的长度$l_i$,每走$1$的长度需要$1$分钟. 一开始所有红绿灯都是绿色的,$g$分钟后所有红绿灯变 ...
随机推荐
- 笔记-2:python基本数据类型
1.数字类型 1.1 整数类型 整数类型有4种进制表示:十进制,二进制,八进制,十六进制,默认情况下,整数采用十进制. 整数类型有4种进制:十进制. 二进制. 八进制和十六进制. 默认情况, 整数采用 ...
- Python_oneday
基本程序设计 一切代码输入,请使用英文输入法 编写一个简单的程序 圆公式面积: area = radius * radius * 3.1415 在Python里面不需要定义数据的类型 控制 ...
- Docker 常用命令和Dockerfile
Docker 简介 官方的解释为:Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现 ...
- Unity 更改鼠标指针
1. 把鼠标指针图标导入到Unity中,把它的Texture Type改为Cursor : 2. 打开PlayerSettings面板,把鼠标指针图片拖到Default Cursor中: 3. 在场景 ...
- 使用NPOI导出Excel文件
使用NPOI导出Excel文件,本实例使用了ASP.NET MVC. 1.使用NPOI导出Excel文件 实例:导出商品列表. 要求:1.通过NPOI导出导出商品列表信息: 2.使用Excel函数计算 ...
- [转载]机器学习优化方法总结:SGD,Momentum,AdaGrad,RMSProp,Adam
[转载]机器学习优化方法总结:SGD,Momentum,AdaGrad,RMSProp,Adam https://blog.csdn.net/u010089444/article/details/76 ...
- extension(类扩展)和 category(类别)
extension(类扩展) 简单来说,extension在.m文件中添加,所以其权限为private,所以只能拿到源码的类添加extension.另外extension是编译时决议,和interfa ...
- Spring Cloud(四)服务提供者 Eureka + 服务消费者 Feign
上一篇文章,讲述了如何通过RestTemplate + Ribbon去消费服务,这篇文章主要讲述如何通过Feign去消费服务. Feign简介 Feign是一个声明式的伪Http客户端,它使得写Htt ...
- cv2.videocapture()失败,无法读取视频
原因:缺少ffmpeg的支持 解决:一般opencv3.3版本及以上支持ffmpeg,实验4.1.0成功 pip install opencv-python pip install opencv-co ...
- select * 和 select 字段的速度对比
拿WordPress的数据库做一个对比 SELECT ID,post_title, post_author FROM wp_posts ORDER BY ID LIMIT 100; OK, Time: ...