标签:用不了 网上 线段 inline 必须 空间 boa bre 最长前缀
简要总结下比赛情况。
开局还是有点坑,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的,包括我在内。现在的测试数据已经非常的强了,常见的模数和进制基本都用不了了??。
给出一个整数\(n\),请你构造一个正整数\(s\)使得十进制中\(s\)的位数为\(n\),并且
? 1、每一位都不为\(0\)
? 2、被每一位的数字整除
\((1≤n≤10^5)\)
很明显就构造\(2999...9\)或者\(23333...3\)即可。
签到题就不重新写代码了~~(懒)~~代码传送门
对于一个长度为\(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) $,数据保证有自然数序列解。
我们移项之后就会发现\(a_i=b_i+\max(0,a_1,...,a_{i-1})\)是个状态转移方程,按位求就好。
还是放现场代码代码传送门
给你一个长度为\(n\)的全排列\(p_1,p_2,…,p_n\),让你严格分割成\(k\)个非空区间。使得划分的\(k\)个区间中,(每个区间的最大值)之和最大。输出这个求和的最大值和有多少种这样的划分。后者对\(998244353\)取模。
\((1≤??≤??≤2\times10^5)\)
既然全排列分割成\(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\)种。
具体实现用了一下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;
}
给你一个由小写字母组成的字符串\(s\),让你求一个字符串\(t\)。使得\(t\)是一个回文串,且\(t\)能表示为\(s\)的一个前缀+\(s\)的一个后缀,并且\(|t|\leq|s|\)。
\((1≤|s|≤10^{6})\)
很明显,如果\(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|)\)。完整代码如下。
#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;
}
标签:用不了 网上 线段 inline 必须 空间 boa bre 最长前缀
原文地址:https://www.cnblogs.com/leachim/p/12549842.html