日期:2023年10月4日星期三
学号:S07731
姓名:魏仲毅
1.比赛概况:
比赛总分共4题,满分400。
赛时拿到0分(………………)。
2.比赛过程:
一开始看第一题是自己比较擅长的模拟,就努力解第一题,但1小时还是乱码输出,心态炸裂。
第二题找到思路,但你没读好题。
第三第四题啥也读不懂,直接放弃。
3.题解报告:
(1) 第一题:复读机(repeater)
重点: 模拟。
情况:赛中0分,已补题。
题意:这个字符串中可能包含多个数字,当多次出现数字时,例如a5b2,我们从左到右解析这个字符串,a5表示将 aa 复读 5 遍,即原字符串变 为aaaaab,然后遇到数字2,再将所有消息全部复读2遍,即aaaaabaaaaab。
赛时本题做题想法:想到统计所有的字母和数,但是没有操作好。
题解:答案字符串正常拼接,如果是数字,就找到数字下标是t,累加完成后需要先退到最后一个数字字符,因为外层for会增加到第一个非数字字符。之后由于本身算了一遍,就自减一。最后拼接t次前面的ans字符串,结束输出即可。
AC 代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
//freopen("repeater.in","r",stdin);
//freopen("repeater.out","w",stdout);
int t;
cin>>t;
while(t--){
int n,idx1=1;
string s,a,c;
cin>>n>>s;
s=" "+s,a="";
for(int i=1;i<=n;i++){
if(s[i]<='z'&&s[i]>='a'){
a+=s[i];
}
else{
int sum=0;
int t=i;
while(s[t]<='9'&&s[t]>='0'){
sum=sum*10+s[t]-'0';
t++;
}
i=t-1;
sum--;
c=a;
while(sum--){
a+=c;
}
}
}
cout<<a<<endl;
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
(2) 第二题:小可的矛与盾(spearshield)
重点:前缀和。
情况:赛中0分,已补题。
题意:有n个战士站成一排,他们的编号从1到n,每个战士都有一个战斗力xi=i,但是小可们有不同的分工,有的充当队伍的矛,有的充当队伍的盾,矛的攻击力和盾的防御力和小可本身的战斗力相同。我们要将战士们分成两个阵营,编号[1,pos]为第一阵营,第一阵营中我们只考虑矛的攻击力总和w,编号[pos+1,n]为第二阵营,第二阵营中我们只考虑盾的防御力总和v,请问对于所有的0≤pos≤n+1,|w-v|的值最小,请问|w-v|最小为多少。
赛时本题做题想法:想到用前缀和去求解但是没有注意xi=i,只加了1。
题解:分别求矛和盾的前缀和,再去找断点,分矛和盾,找到最小值后输出即可。
AC 代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,a[100005],s1[100005],s2[100005];
int minn=2147483646;
string s;
int main(){
//freopen("spearshield.in","r",stdin);
//freopen("spearshield.out","w",stdout);
cin>>n>>s;
for(int i=1;i<=n;i++){
a[i]=s[i-1]-'0';
}
for(int i=1;i<=n;i++){
if(a[i]==0){
s1[i]=s1[i-1]+i+1;
s2[i]=s2[i-1];
}
if(a[i]==1){
s2[i]=s2[i-1]+i-1;
s1[i]=s1[i-1];
}
}
for(int i=1;i<=n;i++){
minn=min(minn,abs(s1[i]+s2[i]-s2[n]));
}
cout<<minn;
//fclose(stdin);
// fclose(stdout);
return 0;
}
(3) 第三题:不合法字符串(illegality)
重点:思想。
情况:赛中0分,已补题。
题意:在给出若干个不合法的字符串s[i],和一篇小说str,小可需要把str中的不合法字符串用"*"和谐掉。当然小可是一个很聪明的审核员,他会用最少的*和谐字符串。
赛时本题做题想法:想不到如何做,只好输出样例。
题解:首先,我们要明确:
1.如果单词按长度排序后 改每个单词的最后,不能保证OK ab baa baab 则多改了
2.如果直接遍历到单词改,也不合适
3.对小说遍历,如果找到某单词的结尾则修改,这样能防止了前后缀的问题。
我们可以让小说下标从1开始,这样方便写代码,遍历整个小说和不和谐的单词。如果这个单词不包含在前面,越过,寻找下一个。找到单词后只修改结尾。最后输出修改后的小说即可。
AC 代码:
#include<bits/stdc++.h>
using namespace std;
int T,n,m;
string str[100000],tgt;
int main(){
cin>>T;
while(T--){
cin>>m;
for(int i=1;i<=m;i++
cin>>str[i];
cin>>tgt;
n=tgt.length();
tgt=' '+tgt;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i<str[j].length()) continue;
if(tgt.substr(i-str[j].length()+1,str[j].length())==str[j]){
tgt[i]='*';
}
}
}
for(int i=1;i<=n;i++)
cout<<tgt[i];
cout<<endl;
}
return 0;
}
(4) 第四题:虚假的珂朵莉树(kodori)
重点:dfs。
情况:赛中0分,已补题。
题意:小可有一棵树,有 n 个节点,根节点为 1, 每个节点都有一个权值。设每个节点与根节点距离是这个节点的深度。小可会在这棵树上增加 m 条虚假边,任意一条虚假边不会和原来的树边或其他虚假边重合(增加的虚假边不影响节点深度)。之后小可会进行 q 次操作:操作1: 让结点 u 的权值增加 k ,并对与结点 u 相邻的结点中,深度比结点 u 小的结点重复操作1。操作2:让结点 u 的权值增加 k ,并对与结点 u 相邻的结点中,深度比结点 u 大的结点重复操作2。小可想知道,经过 q 次操作之后,所有的节点的权值是多少?
赛时本题做题想法:没时间了,也没有思路,就过了一个样例。
题解:应使用dfs来做。首先,可以发现,我们只需要查询一次,并且每个操作之间无关,那么我们可以在做完所有操作后再进行计算。其次,若是对同一个点进行多次操作一,可以将这多次操作一合成一次操作一,操作一的权值为多次操作一的权值和,而由其他结点传递过来的操作一也可以和当前结点的操作一进行合并。操作二同理。用两个桶标记数组即可。
对于操作一,可以发现,深度最大的结点不会受到对其他结点进行操作一的影响,那么深度最大的结点操作完后,深度次大的结点就不会再次受到影响。
依此类推,我们可以根据结点深度从大到小进行操作一,这样只需要进行一次遍历即可完成所有的操作一操作。对于操作二,类似于操作一,可以根据结点深度从小到大进行操作。因此,我们只需要先dfs或bfs处理出各个结点的深度,然后将操作进行记录,在up和down数组中记录操作一和操作二累积的权值,再根据操作按深度从小到大或从大到小进行传递,最后将up、down数组与原本的权值数组进行求和输出即可。
1. 打表每次操作的up增加值、down增加值,并不更新其他相邻的节点。
2. 对于操作一,从深度最大的节点开始往上更新up值。
3. 对于操作二,从深度最小的节点开始往下更新down值。
AC 代码:
#include<bits/stdc++.h>
using namespace std;
const long long p=1e9+7;
struct node{
int to,next;
}e[5000005];
vector<pair<int,int> > g;
long long a[1000005],up[1000005],down[1000005];
int n,m,q,cnt,head[1000005],d[1000005];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int u,int fa){
g.push_back(make_pair(d[u],u));
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa) continue;
d[v]=d[u]+1;
dfs(v,u);
}
}
int main(){
cin>>n>>m>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
dfs(1,1);
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
for(int i=1;i<=q;i++){
int pd,u,v;
cin>>pd>>u>>v;
if(pd==1) up[u]=(up[u]+v)%p;
else down[u]=(down[u]+v)%p;
}
sort(g.begin(),g.end());
for(int i=0;i<g.size();i++){
int x=g[i].second;
for(int j=head[x];j;j=e[j].next){
int y=e[j].to;
if(d[y]>d[x]) down[y]=(down[y]+down[x])%p;
}
}
reverse(g.begin(),g.end());
for(int i=0;i<g.size();i++){
int x=g[i].second;
for(int j=head[x];j;j=e[j].next){
int y=e[j].to;
if(d[y]<d[x]) up[y]=(up[y]+up[x])%p;
}
}
for(int i=1;i<=n;i++) cout<<(a[i]+up[i]+down[i])%p<<" ";
return 0;
}
4. 赛后总结:
本次比赛出现了时间不够用,思路不够开拓,没有读明白题,心态不够好的问题,以后需要合理规划时间,前两道题可以用时1.5小时,不要被一道题卡住太久。如果没有思路就有两种选择:
1.暴力拿小数据的分。
2.多往自己学过的地方去想,说不定就有意外惊喜。
如果一道题卡40分钟左右就先“放弃”,先去解其他的题,说不定第二题就比第一题简单。
明天最后一天,必须争一口气,加油!