第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛题解
先抱怨一下,这场比赛的题锅太多了,而且正赶上状态不好,Ac 1/12就离谱。。
H 数羊
给定n,m(1≤n≤109,0≤m≤2)n,m(1\leq n \leq 10^9,0\leq m \leq 2)n,m(1≤n≤109,0≤m≤2),计算A(n,m)A(n, m)A(n,m),以下给出A(n,m)A(n,m)A(n,m)公式:
{A(1,0)=2A(0,m)=1m≥0A(n,0)=n+2n≥2A(n,m)=A(A(n?1,m),m?1)n,m≥1\begin{cases} A(1,0)=2\\ A(0,m) = 1 \quad m \geq 0\\ A(n,0)=n+2 \quad n \geq 2 \\ A(n,m) = A(A(n-1, m), m-1)\quad n,m \geq 1 \end{cases} ??????????A(1,0)=2A(0,m)=1m≥0A(n,0)=n+2n≥2A(n,m)=A(A(n?1,m),m?1)n,m≥1?
做法非常多的简单题:不管是打表还是按照公式推都可做。但关键在于mmm的取值范围(一定要注意mmm的范围,不然打表轻松stackoverflow,我说怎么大家都说打表可做,我就stackoverflow了。。。)
在mmm的取值范围内,打表即可找出规律:
m=0时,A(n,0)=n+2;m=1时,A(n,1)=2?nm=2时,A(n,2)=2nm=0时,A(n,0)=n+2;\\ m=1时,A(n,1)=2\cdot n\\ m=2时,A(n,2)=2^nm=0时,A(n,0)=n+2;m=1时,A(n,1)=2?nm=2时,A(n,2)=2n
D Seek the joker I
n(n≤105)n(n\leq 10^5)n(n≤105)张扑克牌的牌堆,最下面一张是龟牌,抽到的人输。现在每人每次至多抽k(k≤105)k(k \leq 10^5)k(k≤105)张,至少抽一张。判断先/后手必胜。声明:本题中的所有角色在剧情发生时均已超过18岁。
yo xi no forever!
一个简单的巴什博弈。抽到第nnn张的人输,说明胜者抽走了第n?1n-1n?1张。
套一下巴什博弈公式即可
E Seek the joker II
n(n≤105)n(n\leq 10^5)n(n≤105)张扑克牌的牌堆,从上往下第xxx张是龟牌,抽到的人输。现在每人可以在牌堆底/顶/同时抽任意张,但至少抽一张,并且在牌堆上下同时抽必须抽走相同数目的牌。判断先/后手必胜。声明:本题中的所有角色在剧情发生时均已超过18岁。
yo xi no forever!
一道威佐夫博弈题。但我当时没学过威佐夫博弈,还当是尼姆博弈变形来做。。。
龟牌将牌堆分为两部分,一部分为x?1x-1x?1张,一部分为n?xn-xn?x张。
引入概念:
局势:用(ak,bk)(a_k,b_k)(ak?,bk?)表示两堆牌中剩余牌的数量。称之为局势
奇异局势:如果处于某种局势下的一方必输,则称为奇异局势。
威佐夫博弈的结论是:
- 第iii个奇异局势的差值是iii
- 每一个状态的第一个都是以前没有出现过的最小非负整数。
- ak=[k(1+√5)2],bk=ak+k(k=0,1,2,…,n)a_k =[\frac{k(1+√5)}{2} ],b_k= a_k + k (k=0,1,2,…,n )ak?=[2k(1+√5)?],bk?=ak?+k(k=0,1,2,…,n)为奇异局势的通式。
证明不会,贴个外链吧[证明]
判断:判aka_kak?是否等于两数差值乘以黄金分割比向下取整即可
#include<bits/stdc++.h>
using namespace std;int n, m;
const double lorry = (sqrt(5.0) + 1.0) / 2.0;int main() {
cin >> n >> m;if(n < m) swap(n, m);int a = n - m;if(m == int(lorry * (double)a))cout << "NO" << endl;else cout << "YES" << endl;
}
I 买花
给定一个数nnn,即需要买nnn多花,后一天买的花数为前一天两倍。问是否能在k(1<k≤15)k(1<k\leq 15)k(1<k≤15)天内恰好买到nnn朵花。
假设第一天买xxx朵花,考察以下等比数列:
x,2x,4x,...,2k?1xx,2x,4x,...,2^{k-1}xx,2x,4x,...,2k?1x,其和为x(2k?1)x(2^k-1)x(2k?1)。因为kkk有范围,我们可以枚举kkk,然后判断对于给定的nnn,是否存在xxx(也就是整除)。
(鬼知道我当时脑子抽了什么风没想到整除。。。)
C 上进的凡凡
给定一个长为n(1≤n≤105)n(1\leq n \leq 10^5)n(1≤n≤105)数组,判断数组的非降序子数组的个数。子数组定义为:在原数组头尾删除任意元素和得到的数组。子数组包含原数组,不包含空串。
我当时写的代码事后一看过了九成的测试用例。那么究竟是错哪了呢?
我爆int了
三年竞赛一场空,不开longlong见祖宗
sum += cnt * (cnt - 1) / 2 + cnt
cnt是用来计算非降序的长度的,按理说n≤105n\leq 10^5n≤105,cnt不会爆int,但是。。
上面的代码,sum是longlong,cnt是int。但c艹规定,int?intint * intint?int 还是int,然后就爆了。
思路很简单,直接贴一下ac代码吧。
代码:
#include <bits/stdc++.h>
using namespace std;const int maxn = 1e5 + 10;
long long a[maxn];int main()
{
int n;scanf("%d", &n);long long cnt = 0;long long sum = 0;for (int i = 1; i <= n; i++){
scanf("%lld", &a[i]);if (a[i] >= a[i - 1]) {
cnt++;}else {
sum += cnt * (cnt - 1) / 2 + cnt;cnt = 1;}}sum += (long long)cnt * (cnt - 1) / 2 + cnt;printf("%lld", sum);return 0;
}
B 小宝的幸运数组
如果一个数组的和能够整除幸运数字k(1≤k≤105)k(1\leq k \leq 10^5)k(1≤k≤105),那么称该数组为幸运数组。给定一个长度为n(1≤n≤105)n(1 \leq n \leq 10^5)n(1≤n≤105)的数组,求这个数组中最长幸运子数组的长度。
为了写这题,我们先看一个简单一点的题目:
给定数组aaa,一个整数kkk。判断aaa中满足ai+aj=ka_i+a_j=kai?+aj?=k的有多少对?
思路:
两个for循环的复杂度显然是O(n2)O(n^2)O(n2)。如何优化呢?可以用一个hash。对于每一个aaa,在hash table里查询是否有k?ak-ak?a。复杂度可以达到O(n)O(n)O(n)预处理加O(nlogn)O(nlogn)O(nlogn)的n次查询,总复杂度为O(nlogn)O(nlogn)O(nlogn)。
回过来再看这题:既然要求一个数组连续一段的和,那必然要维护前缀和。
然后我们必须用到一些数论知识:(a?b)%m=(a%m?b%m)%m(a-b)\%m=(a\%m-b\%m)\%m(a?b)%m=(a%m?b%m)%m
知道这些,这道题就可做了。一个数组从第lll项到第rrr项的和为:
∑i=lrai=∑i=1rai?∑i=1l?1ai\begin{aligned} \sum_{i=l}^ra_i = \sum_{i=1}^ra_i-\sum _{i=1}^{l-1}a_i \end{aligned} i=l∑r?ai?=i=1∑r?ai??i=1∑l?1?ai??
数组的和能够整除kkk,也就是模kkk等于000。
∑i=lrai%k=0=>(∑i=1rai%k?∑i=1l?1ai%k)%k=0\begin{aligned} &\sum_{i=l}^ra_i\%k = 0\\ =>&(\sum_{i=1}^ra_i\%k-\sum _{i=1}^{l-1}a_i\%k)\%k=0 \end{aligned} =>?i=l∑r?ai?%k=0(i=1∑r?ai?%k?i=1∑l?1?ai?%k)%k=0?
而∑i=1rai\sum_{i=1}^ra_i∑i=1r?ai?和∑i=1l?1ai\sum _{i=1}^{l-1}a_i∑i=1l?1?ai?都是数组aaa的前缀和(取模的前缀和)。我们想要数组的和模kkk为000,就相当于求使得aaa在i,ji,ji,j处前缀和相等的最大i,ji,ji,j差值。
判断:一边读取数据,一边算前缀和,如果这个前缀和(注意需要取模)不在map中,那就存进去(把前缀和映射到下标)。如果这个前缀和在map中,就算当前下标和map中前缀和的下标并做差。
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;const int maxn = 1e5 + 10;
long long a[maxn];int main()
{
int t;scanf("%d", &t);while (t--) {
unordered_map<int, int> mp;int k, n;scanf("%d%d", &n, &k);long long sum = 0;long long dist = 0;mp[0] = 0;for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);sum = (a[i] % k + sum) % k;if (mp.count(sum)) {
dist = std::max(dist, (long long)i - mp[sum]);}else {
mp[sum] = i;}}if (dist != 0) {
printf("%lld\n", dist);}else {
printf("-1\n");}}return 0;
}
G 贪吃的派蒙
给定一个数组,长度为n(2≤n≤105)n(2\leq n \leq 10^5)n(2≤n≤105),数组的第iii个元素为aia_iai?,表示编号为iii的角色可以至多领取aia_iai?份花酿鸡.数组中最大的一个aka_kak?对应的是派蒙,派蒙每次只能拿aka_kak?份花酿鸡.花酿鸡一共有kkk份,第一个没有花酿鸡领的人要洗碗.现在大家拍好队,给定的数组就是顺序.大家都想让派蒙洗碗,判断这是否可能.
这都能迫害派蒙是没想到的,不过…迫害的好
现在大家按照这样的顺序排队,其中aka_kak?是派蒙:a1,a2,a3,...,ak,...,an?2,an?1,ana_1, a_2,a_3,...,a_k,...,a_{n-2},a_{n-1},a_na1?,a2?,a3?,...,ak?,...,an?2?,an?1?,an?.如果用ccc表示第一个人领取花酿鸡的次数,x1,x2,x3,...,ak,...,xn?2,xn?1,xn(1≤xi≤ai,这里除了派蒙每次都是一个定值以外其他的都是不固定的)x_1, x_2,x_3,...,a_k,...,x_{n-2},x_{n-1},x_n(1\leq x_i \leq a_i,这里除了派蒙每次都是一个定值以外其他的都是不固定的)x1?,x2?,x3?,...,ak?,...,xn?2?,xn?1?,xn?(1≤xi?≤ai?,这里除了派蒙每次都是一个定值以外其他的都是不固定的)表示每人每次领取花酿鸡的个数.那么想要派蒙刷碗,也就是到派蒙这刚好没有花酿鸡,需要满足
k=c?(x1+x2+...+xk?1)+(c?1)?(ak+xk+1+...+xn)=>k?(c?1)ak=c?(x1+x2+...+xk?1)+(c?1)?(xk+1+...+xn)k = c\cdot (x_1+x_2+...+x_{k-1})+(c-1)\cdot(a_k+x_{k+1}+...+x_n)\\ =>k - (c-1)a_k=c\cdot (x_1+x_2+...+x_{k-1})+(c-1)\cdot(x_{k+1}+...+x_n) k=c?(x1?+x2?+...+xk?1?)+(c?1)?(ak?+xk+1?+...+xn?)=>k?(c?1)ak?=c?(x1?+x2?+...+xk?1?)+(c?1)?(xk+1?+...+xn?)
这里的xix_ixi?都是有范围的,1≤xi≤ai1\leq x_i \leq a_i1≤xi?≤ai?带入得:
(c?1)n+k?c≤k?(c?1)ak≤c∑i=1k?1ai+(c?1)∑i=k+1nai=>(c?1)(n+ak)+k?c≤k≤c∑i=1k?1ai+(c?1)∑i=k+1nai+(c?1)ak\begin{aligned} (c-1)n+k-c\leq k - (c-1)a_k\leq c\sum_{i=1}^{k-1}a_i+(c-1)\sum_{i=k+1}^{n}a_i\\ =>(c-1)(n+a_k)+k-c\leq k \leq c\sum_{i=1}^{k-1}a_i+(c-1)\sum_{i=k+1}^{n}a_i+(c-1)a_k \end{aligned} (c?1)n+k?c≤k?(c?1)ak?≤ci=1∑k?1?ai?+(c?1)i=k+1∑n?ai?=>(c?1)(n+ak?)+k?c≤k≤ci=1∑k?1?ai?+(c?1)i=k+1∑n?ai?+(c?1)ak??
看着好像挺吓人,但仔细想一想,这个式子中的未知量应该只有ccc.也就是说,这是一个关于c的不等式组(左面右面各一个不等式).判ccc是否有正整数解即可.
继续化简即可得到下式(比上面那个漂亮多了):
k+ak+∑i=k+1nai∑i=1nai≤c≤n+akn+ak?1\begin{aligned} \frac{k+a_k+\sum^{n}_{i=k+1}a_i}{\sum_{i=1}^{n}a_i} \leq c \leq \frac{n+a_k}{n+a_k-1} \end{aligned} ∑i=1n?ai?k+ak?+∑i=k+1n?ai??≤c≤n+ak??1n+ak???
J 这是一题简单的模拟
K 黑洞密码
这两道都是纯模拟,考验底力的题(对我这种fw是真的难,照着官方题解抄都wa了4发)