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\times 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\times10^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\}\)

标记最大数:\(\{\textbf 8,3,4,\textbf 9,5,1,\textbf7,\textbf{10},2,6\}\)

发现切的位置就是\(8-9,9-7,7-10\)这三处地方,分别有\(3,3,1\)种切法。

利用乘法原理可以算出来最多有多少种这样的划分,即\(3\times 3\times 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\n",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\)的前缀\(\text {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\n", 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\n", &T);
	while(T--){
		solve();
	}
	return 0;
}
posted @ 2020-03-23 02:54  Leachim  阅读(167)  评论(0编辑  收藏  举报