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 总结

Codeforces Global Round 7

Codeforces Global Round 7 题解

Codeforces Global Round 7 D2. Prefix-Suffix Palindrome (Hard version) -- manacher

Codeforces Global Round 7 D2. Prefix-Suffix Palindrome (Hard version)(Manacher算法+输出回文字符串)