日期:2023 年 10 月 2 日 学号:S08503 姓名:徐浩轩
比赛概况: 比赛总分共 4 题,满分 400,赛时拿到 70 分,其中第一题 40 分,第二题 30 分,第三题 0 分,第四题 0 分。
比赛过程: 先用模拟写了A题40分,B题根据部分样例骗了30分,CD题偷分失败,未得分。
题解报告:
(1) 第一题:答案错误:赛中 40 分,已补题
题意:给定A数组,求满足条件的B数组最小字典序是多少 赛时本题做题想法:模拟
AC 代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[100005];
map<int,int> vis;
map<int,int> num;
int main(){
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
vis[a[i]]=1;
}
int cnt=1;
for(int i=1;i<=n;++i){
if(num.count(a[i])==0){
while(vis.count(cnt)){
cnt++;
}
vis[cnt]=1;
num[a[i]]=cnt;
}
}
for(int i=1;i<=n;++i){
cout<<num[a[i]]<<" ";
}
return 0;
}
(2) 第二题:答案错误 :赛中 70 分,已补题
题意:给定5个整数n,m,p,t,k,求最大的知识能量。
赛时本题做题想法:模拟
AC 代码:
#include<bits/stdc++.h>
using namespace std;
long long n,m,k,q,t,sum=0;
int main(){
cin>>n>>m>>k>>q>>t;
if(m<k){
cout<<"0";
return 0;
}
if(n>m/k){
n=m/k;
}
long long l=m%n;
long long ans1=l*min((m/n+1)*t,q);
long long ans2=(n-l)*min((m/n)*t,q);
cout<<ans1+ans2;
return 0;
}
(3) 第三题:答案错误:赛中 50 分,已补题
题解:区间最大值和最小值的绝对值相等,有以下两种情况:
1.区间中只有同一种数字。这个比较简单,通过简单的 for 循环向后计数得到。
2.区间中最大值和最小值为相反数。
第二种情况具体来说,就是最大值 ,最小值 ,和最大值 ,最小值 。
考虑如何求解第二种情况。
考虑固定左端点时,如何找合法的右端点:
1.对于最大值为1最小值-1的情况:右端点需要保证:区间有1和-1,区间中没有2和-2.
这可以通过维护左端点向后的1/-1/2/-2的初始位置找到。右端点的起始位置为max
(下一个1的位置,下一个-1的位置),结束位置为 min (下一个 的位置,下一个 的位置)
2.对于最大值为 最小值 的情况:右端点需要保证:区间中有 和 。维护方法类似上面。
至此本题做完,复杂度为 O(n)
AC 代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e5+5;
const int inf=0x3f3f3f3f;
ll n,ans,num[N];
int nxt[N][5];
int startpos,endpos;
int main(){
cin>>n;
memset(nxt,0x3f,sizeof(nxt));
for(int i=1;i<=n;++i){
cin>>num[i];
}
ll ret=1,lst=num[1];
for(int i=2;i<=n;++i){
if(num[i]==lst){
++ret;
}
else{
ans+=ret*(ret+1)/2;
ret=1;
lst=num[i];
}
}
ans+=ret*(ret+1)/2;
for(int i=n;i>=1;--i){
for(int j=0;j<=4;++j){
nxt[i][j]=nxt[i+1][j];
}
nxt[i][num[i]+2]=i;
int maxpos1=nxt[i][1+2],maxpos2=nxt[i][2+2];
int minpos1=nxt[i][-1+2],minpos2=nxt[i][-2+2];
startpos=max(maxpos2,minpos2);
endpos=n+1;
if(startpos!=inf&&startpos<endpos){
ans+=endpos-startpos;
}
startpos=max(maxpos1,minpos1);
endpos=min(min(maxpos2,minpos2),(int)n+1);
if(startpos!=inf&&startpos<endpos){
ans+=endpos-startpos;
}
}
cout<<ans;
return 0;
}
(4) 第四题:答案错误:赛中 0 分,已补题
题解:30%数据下:可以直接暴力枚举每个点作为根的情况,然后dfs出每个点到根的距离,计算方差。
另外的20%数据下:为一条链表的情况,那么根设置在中间的位置是优的。
AC 代码:
#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
const int maxn=1e5+10;
ll sum2[maxn],sum1[maxn],sz[maxn],n,res;
vector<int> g[maxn];
void dfs1(int u,int fa){
for(int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v==fa){
continue;
}
dfs1(v,u);
sz[u]+=sz[v];
sum1[u]+=sum1[v];
sum2[u]+=sum2[v];
}
sum2[u]+=sz[u]+2*sum1[u];
sum1[u]+=sz[u];
sz[u]++;
return ;
}
void dfs2(int u,int f,ll s1,ll s2){
res=min(res,n*(s2+sum2[u])-(sum1[u]+s1)*(sum1[u]+s1));
for (int i=0;i<g[u].size();i++){
int v=g[u][i];
if(v==f){
continue;
}
ll ret1=sum1[u]-(sum1[v]+sz[v])+s1;
ll ret2=sum2[u]-(sum2[v]+2*sum1[v]+sz[v])+s2;
ll szu=n-sz[v];
dfs2(v,u,ret1+szu,ret2+2*ret1+szu);
}
return;
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;++i){
g[i].clear();
sum1[i]=sum2[i]=sz[i]=0;
}
for(int i=1;i<=n-1;++i){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
res=LONG_LONG_MAX;
dfs1(1,0);
dfs2(1,0,0,0);
cout<<res<<"\n";
}
return 0;
}
赛后总结: 本次比赛出现了题目名写错丢分的问题,以后需要注意题目名,以防不必要的丢分。