“Shopee杯” E 起来编程暨武汉大学 2020 年大学生程序设计大赛(网络预选赛)解题报告
比赛地址:“Shopee杯” e起来编程暨武汉大学2020年大学生程序设计大赛(网络预选赛)
整场比赛体验极差,英语杀我(题目都看不懂还做个屁啊!!!),结束后当天晚上讲题,对照题解自己又去做了一遍。
E-Yu is a Brutal Creature
题意
找出
解题思路
根据平方差公式:
于是可知
符合条件的数,只有
所以当
#include <bits/stdc++.h>
using namespace std;
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
printf("%d\n", n ? (n - 1) : n);
}
return 0;
}
B-Best Match
题意
给定一个数组,求有多少个数对
解题思路
记录下数组中每个权值的出现次数,记数组中权值
#include<bits/stdc++.h>
using namespace std;
int read(){
int c=0,nx,sign=1;
while(!isdigit(nx = getchar()))
if(nx=='-')
sign=-1;
while(isdigit(nx))
c=c*10+nx-'0',nx=getchar();
return sign*c;
}
const int N = 5e5 + 20;
long long cnt[100];
int main(){
int n = read();
for(int i=1;i<=n;i++)
cnt[read() + 20]++;
long long ans = cnt[20] * (cnt[20] - 1) / 2;
for(int i=1;i<=20;i++)
if(cnt[i + 20] and cnt[20 - i])
ans += cnt[i + 20] * cnt[20 - i];
printf("%lld",ans);
}
A-A Monument For Heroes
题意
给你若干字符串,求按照首尾字母相同的方式接龙能接上多少个,且必须按照题目输入的顺序接, 也就是先出现的字符串必须接在前面。
解题思路
使用 DP 实现,记
随后依次枚举每个字符串,假设字符串
更新所有
#include<bits/stdc++.h>
#define inf 1<<29
#define maxn 1000010
typedef long long ll;
using namespace std;
int n,mp[210][210],ans;
char str[110];
int main(){
cin>>n;
for(int i=1;i<=n;++i){
scanf("%s",str);
int len=strlen(str);
char s=str[0],t=str[len-1];
for(int j='a';j<='z';++j){
if(mp[j][s]){
mp[j][t]=max(mp[j][t],mp[j][s]+len);
}
}
mp[s][t]=max(mp[s][t],len);
}
for(int i='a';i<='z';++i) ans=max(ans,mp[i][i]);
cout<<ans<<endl;
return 0;
}
D-DIY Masks at Home
题意
给你一个由大写字母构成的二维矩阵,你需要找到一个最大的正方形,使得这个正方形内只包含一种字母。
解题思路
本题实际上有多种通过方法,下面介绍两种参考方法:
- (暴力哈希)将原矩阵内每一种字母都替换成一个素数,然后计算这个矩阵的二维前缀积(对大素数取模)。那么在给定二分长度 k 的前提下,我们每次可以枚举一个矩形的左上角
,利用逆元计算出这么个矩形的积,再和这种字母的纯 正方形对应的哈希值比对。如果担心碰撞,只需改成双哈希就好。总复杂度为常数有点大的 。 - 我们如果在原矩阵 F 的基础上预处理出一个新矩阵
, 第 行第 列的值的意义为:这个值在这一行前面有多少个连续的数和它相同(包括自己)。
随后我们对于每一列从上到下遍历,如果一个边长为 ,右下角位于 的矩形存在的话,一定会满足: 可想而知,对于我们枚举的右下角,右上角也是具备单调性的,所以我们可以采用二分 + 对每一列维护 数组的方法获得一个 的方法。
#include<bits/stdc++.h>
#define inf 1<<29
#define maxn 1000010
typedef long long ll;
using namespace std;
int n,mp[210][210],ans;
char str[110];
int main(){
cin>>n;
for(int i=1;i<=n;++i){
scanf("%s",str);
int len=strlen(str);
char s=str[0],t=str[len-1];
for(int j='a';j<='z';++j){
if(mp[j][s]){
mp[j][t]=max(mp[j][t],mp[j][s]+len);
}
}
mp[s][t]=max(mp[s][t],len);
}
for(int i='a';i<='z';++i) ans=max(ans,mp[i][i]);
cout<<ans<<endl;
return 0;
}
C-Can You Help ZSGW
题意
有一个排列,已知我们对于这个排列执行单调栈算法过程中,遍历到每一个位置之后单调栈的大小,有些位置缺失可以任意。求一个满足这种情况的字典序最小的排列。
解题思路
首先我们应该做的事,是补全这个单调栈数组
- 若
,则 ,且 。 - 对于
,一定有 。
我们从左到右依次补全每一个为
- 若
, 。 - 否则,如果我们填一个比
小的数的话,就意味着之后补全的时候 ,在字典序上不会是个好主意。所以应该填 。
补全数组之后,规律如下:
首先我们可以发现所有为
#include <bits/stdc++.h>
using namespace std;
int T, n;
int p[200005];
int lst[200005], tail;
int pre[200005];
int nxt[200005];
int s[200005];
int main() {
scanf("%d", &T);
for(int k = 0; k < T; ++k) {
scanf("%d", &n);
for(int i = 0; i <= n; ++i) nxt[i] = pre[i] = 0;
tail = 0;
for(int i = 1; i <= n; ++i) scanf("%d", &p[i]);
p[1] = 1;
for(int i = 2; i <= n; ++i) if(p[i] == -1) {
if(p[i + 1] - p[i - 1] == 2) p[i] = p[i - 1] + 1;
else p[i] = p[i - 1] + 1;
}
for(int i = 1; i <= n; ++i) {
if(p[i] > p[i - 1]) {
nxt[tail] = i;
pre[i] = tail;
tail = i;
} else {
int x = lst[p[i]];
nxt[pre[x]] = i;
pre[i] = pre[x];
nxt[i] = x;
pre[x] = i;
}
lst[p[i]] = i;
}
for(int i = 1, j = nxt[0]; i <= n; ++i, j = nxt[j]) s[j] = i;
for(int i = 1; i <= n; ++i) printf("%d ", s[i]);
printf("\n");
}
return 0;
}
v1.5.1