Codeforces Global Round 7
Posted leachim
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Global Round 7相关的知识,希望对你有一定的参考价值。
Codeforces Global Round 7
简要总结下比赛情况。
开局还是有点坑,CF官网炸了,然后镜像也炸了,磨蹭了几分钟才看到题,还只能在m1网上看QAQ。
不过前期勉强算是顺利签到,至少都是首A。
一看D,发现是个字符串的题(我字符串好多没学)。不过发现是个回文串判断,想都没想直接上hash。发现动态修改,直接拷贝了个线段树过来(我也不知道当时为啥脑残了没想到直接按位不需要线段树)。三下五除二(折腾了1个小时。。)似乎写完了,结果MLE制裁。强行压下来空间,然后WA pretest3.找了半天不知道哪错了。不管了换树状数组,又折腾了半个小时换了树状数组。终于A了D1(还不如写暴力,分都被扣的没几分了,我还想着这把上个段呢QAQ太惨了我)。结果D2在4测试点TLE了。。。。。我自己随手搞了个数据本地测了下,果然TLE3秒才出来。最开始以为是模数太大(1e9+9),我换了一个小一点的质数(9191891),然后还是TLE。最后突然想起来可能是long long太慢了,我换成了int之后终于吧时间卡过去了,成功过了pretest。
戏剧性的是。。。。比赛完了之后发现D2被FST了???,然后我把模数换回1e9+9,才A了。。。。居然自己坑了自己一把,为自己扣分扣爆埋下伏笔。虽然比赛结束之后一堆人在hack那些用简单hash的,包括我在内。现在的测试数据已经非常的强了,常见的模数和进制基本都用不了了??。
A. Bad Ugly Numbers
Problem Restatement
给出一个整数(n),请你构造一个正整数(s)使得十进制中(s)的位数为(n),并且
? 1、每一位都不为(0)
? 2、被每一位的数字整除
((1≤n≤10^5))
Solution
很明显就构造(2999...9)或者(23333...3)即可。
Code
签到题就不重新写代码了~~(懒)~~代码传送门
B. Maximums
Problem Restatement
对于一个长度为(n)的自然数序列(a),定义一个长度为(n)的序列(b)满足,(b_i=a_i-max(0,a_1,...,a_{i-1}))
现在给出序列(b),请你反推回序列(a)。
$(1≤??≤2 imes 10^5 , -109≤b_??≤109) $,数据保证有自然数序列解。
Solution
我们移项之后就会发现(a_i=b_i+max(0,a_1,...,a_{i-1}))是个状态转移方程,按位求就好。
Code
还是放现场代码代码传送门
C. Permutation Partitions
Problem Restatement
给你一个长度为(n)的全排列(p_1,p_2,…,p_n),让你严格分割成(k)个非空区间。使得划分的(k)个区间中,(每个区间的最大值)之和最大。输出这个求和的最大值和有多少种这样的划分。后者对(998244353)取模。
((1≤??≤??≤2 imes10^5))
Solution
既然全排列分割成(k)份,很明显如果要让每个区间最大值之和最大,就是让每个区间的最大值最大就好。也就是让这(k)个区间,每个区间独占一个(n)个数之中最大的(k)个数。
所以第一步肯定是找出来(n)个数中最大的(k)个数的位置。
然后考虑分割成(k)个非空区间,其实相当于在(n-1)个空隙中切(k-1)刀。而我们锁定了(k)个最大数的位置之后,其实相当于锁定了每一刀切的范围。
举个例子:(n=10,k=4,a={8,3,4,9,5,1,7,10,2,6})
标记最大数:({ extbf 8,3,4, extbf 9,5,1, extbf7, extbf{10},2,6})
发现切的位置就是(8-9,9-7,7-10)这三处地方,分别有(3,3,1)种切法。
利用乘法原理可以算出来最多有多少种这样的划分,即(3 imes 3 imes 1=9)种。
Code
具体实现用了一下C++ STL里面的map,写的还算是挺好看的吧。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
#define MAXN 200005
#define MOD 998244353
int n,k,a[MAXN];
void solve(){
map<int,int> mp;
scanf("%d %d", &n, &k);
for(int i=1;i<=n;i++){
scanf("%d", &a[i]);
mp[a[i]]=i;
if(mp.size()>k) mp.erase(mp.begin());
}
vector<int> v;
LL sum=0;
for(auto p:mp){
v.push_back(p.second);
sum+=p.first;
}
sort(v.begin(),v.end());
int ans=1;
for(int i=1;i<k;i++){
ans=(1LL*ans*(v[i]-v[i-1]))%MOD;
}
printf("%lld %d
",sum,ans);
}
int main(){
int T=1;
// scanf("%d", &T);
while(T--){
solve();
}
return 0;
}
D. Prefix-Suffix Palindrome
Problem Restatement
给你一个由小写字母组成的字符串(s),让你求一个字符串(t)。使得(t)是一个回文串,且(t)能表示为(s)的一个前缀+(s)的一个后缀,并且(|t|leq|s|)。
((1≤|s|≤10^{6}))
Solution
很明显,如果(s)有一个长度(k)的前缀( ext {pre}(s,k))的逆串刚好是(s)的后缀,那么我们找到最大的这样一个长度(k)。至少可以构造一个长度为(2k)的字符串(t)满足要求。
接着我们很容易发现,如果要构造更长的,我们必须要用到这长度为(k)的前后缀,所以不妨先线性时间判断出来最长的(k)并剔除出(s)字符串。
我们对剩下的字符串,不妨设为(s‘),进行分析,发现我们的目的是找到一个最长的回文前缀或后缀。
那么第一个思路就很容易想到了——哈希。我们知道,一个回文串的逆串等于它本身。所以判断一个串是否为回文串,可以求出它的hash值和它逆串的hash值,O(1)对比两个hash值即可。我们知道,求最长回文前缀可以一位一位的扩充hash,所以总hash时间是线性的。前缀后缀各扫并一次回文串即可,复杂度(O(|s|)),但是太简单的hash后期会被有心人给hack掉,不过比赛时间不够写多hash,所以搏一搏,单车变摩托。这里仅给出求最长回文前缀的核心代码,不多啰嗦。
for(int i=x;i<=len-x-1;i++){
hsh1=(1LL*hsh1*2179%p+(s[i]-‘a‘+1))%p;
hsh2=(1LL*t*(s[i]-‘a‘+1)%p+hsh2)%p;
t=(1LL*t*2179)%p;
if(hsh1 == hsh2)
mx1=max(mx1,i-x+1);
}
第二个思路则比较巧妙。如果我们构造新的字符串(s‘‘=s‘+‘#‘+ overline {s‘}),其中(overline {s‘})是指(s‘)的逆串。我们会发现,我们所求的(s‘)的最长回文前缀,转换成了求(s‘‘)的最长boarder(boarder就是字符串的一个非本身的前缀,满足存在该字符串的后缀与之相等。)。显而易见学过KMP算法的就会想起来KMP的next数组就是存字符串最长前缀。所以我们跑半个KMP(只跑求next的部分)就可以了。复杂度(O(|s|))。完整代码如下。
Code
#include <bits/stdc++.h>
#define LL long long
using namespace std;
#define MAXN 1000005
char s[MAXN],rs[MAXN],ss[MAXN<<1];
int nxt[MAXN<<1];
void solve(){
scanf("%s", s);
int len=strlen(s),x;
for(x=0;x<len/2;x++){
if(s[x]!=s[len-x-1]) break;
}
if(x==len/2){
printf("%s
", s);
return;
}
int n=len-2*x;
int mx1=0;
for(int i=0;i<n;i++){
ss[i]=s[x+i];
ss[n+i+1]=s[x+n-i-1];
}
ss[n]=‘#‘;
nxt[0]=-1;
for(int i=1,k=-1;i<=2*n+1;i++){
while(k!=-1 && ss[k]!=ss[i-1])
k=nxt[k];
nxt[i]=++k;
}
mx1=nxt[2*n+1];
int mx2=0;
for(int i=0;i<n;i++){
ss[i]=s[x+n-i-1];
ss[n+i+1]=s[x+i];
}
ss[n]=‘#‘;
nxt[0]=-1;
for(int i=1,k=-1;i<=2*n+1;i++){
while(k!=-1 && ss[k]!=ss[i-1])
k=nxt[k];
nxt[i]=++k;
}
mx2=nxt[2*n+1];
if(mx1>=mx2){
for(int i=0;i<x+mx1;i++){
putchar(s[i]);
}
for(int i=x-1;i>=0;i--)
putchar(s[i]);
puts("");
}
else{
for(int i=len-1;i>len-x-1-mx2;i--){
putchar(s[i]);
}
for(int i=x-1;i>=0;i--)
putchar(s[i]);
puts("");
}
}
int main(){
int T=1;
scanf("%d
", &T);
while(T--){
solve();
}
return 0;
}
以上是关于Codeforces Global Round 7的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Global Round 7 题解(未完)(ABCD)
Codeforces Global Round 7 D2. Prefix-Suffix Palindrome (Hard version) -- manacher
Codeforces Global Round 7 D2. Prefix-Suffix Palindrome (Hard version)(Manacher算法+输出回文字符串)