日期: 2023 年 10 月 2 日星期一
学号:S07892
1. 比赛概况:
比赛总分共 4 题,满分 400,赛时拿到220分, 其中第一题100分, 第二题100分, 第三题20分, 第四题0分。
2.比赛过程:
第一题看到数据过大时想到用map去做标记数组,中途比较波折,还曾想过用集合去求出有几个非重数,但经过一个半多小时的思考,更改了想法,简洁了自己的代码,第二题就只需要用几个特判(或几个判断表达式),O(1)时间复杂就过了,第三题没能有充分时间阅读题目,直接进行了特殊样例的判断编写,得到了特殊样例的分,第四题缺乏时间,未进行代码编写,赛后准备拷贝代码时电脑还突然崩了,中午又苦苦打了一遍。
3. 题解报告:
(1)第一题:数字对应(digit)
情况:赛中100分
题意:给一个长度为n的序列A,A中每个数找一个对应的正整数对应到B序列,每个数字对应唯一,求出字典序最小的序列B。
赛时本题做题想法:看到数据过大想用map去做标记数组,还想用集合去求出有几个非重数
后来发现求出非重元素个数过于繁琐,将其优化成循环中输出。
题解:运用两个map,一个进行标记,一个存储对应的数字,从小到大循环,判断是否有对应数字,若没有,则从小到大判断是否标记进行查找最小对应。
AC 代码:
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
map<int,int> v,m;
int n,a[100005],b[100005],maxx,minn,mi=1,ma,f;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
int x=b[1];
v[x]=1,minn=x;
for(int i=2;i<=n;i++){
if(b[i]!=x){
x=b[i];
v[x]=1;
maxx=x;
}
}
ma=maxx+1;
for(int i=1;i<=n;i++){
if(m[a[i]]!=0){
printf("%d ",m[a[i]]);
}
else{
if(mi<minn){
printf("%d ",mi);
m[a[i]]=mi;
mi++;
}
else{
if(f==0){
x=0;
for(int j=minn+1;j<maxx;j++){
if(v[j]!=1){
printf("%d ",j);
m[a[i]]=j;
minn=j;
mi=j;
x=1;
break;
}
}
if(x==0){
f=1;
}
}
if(f==1){
printf("%d ",ma);
m[a[i]]=ma;
ma++;
}
}
}
}
return 0;
}
(2)第二题:技能学习(skill)
情况:赛中100分
题意:有n个人,有m份资料,学习至少需要k份资料,每人拥有几份资料每分钟就能产生几个技能点,有t分钟,每人最多得q点,求最大能获得的几点技能点
赛时本题做题想法:因为每人可能会有限制,所以越平均分发资料总收入越高,就尽可能平均分,多出的也一人就一份,简单推出算式加特判就OK了。
题解:数学题,推出计算需要量,加限制特判,相加得出
AC 代码:
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
long long n,m,k,q,t;
long long r,f,e,gp,gt,sum;
int main(){
scanf("%lld%lld%lld%lld%lld",&n,&m,&k,&q,&t);
r=m/k;
if(r>n){
r=n;
}
f=m/r;
e=m-f*r;
if(e==0){
if(q<f*t){
gp=q;
}
else{
gp=f*t;
}
}
else{
if(q<f*t){
gp=q;
gt=q;
}
else{
gp=f*t;
if(q<(f+1)*t){
gt=q;
}
else{
gt=(f+1)*t;
}
}
}
sum=gp*(r-e)+gt*e;
printf("%lld",sum);
return 0;
}
(3)第三题:等于(equal)
情况:赛中20分,已补题
题意:有一个长为n的序列,序列元素一定为-2,-1,1,2中的一个,求出有多少个子序列的最大值的绝对值等于最小值的绝对值
赛时本题做题想法:时间过紧,只看了特殊样例,判断了是否序列所有数相等
题解:固定左端点,情况一,区间内所有数相同,情况二,分成是否含绝对值为2的数的两种情况
AC 代码:
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define ll long long
using namespace std;
ll n,ans,num[500005];
int nxt[500005][5],startpos,endpos;
int main(){
scanf("%lld",&n);
memset(nxt,0x3f,sizeof(nxt));
for(int i=1;i<=n;++i){
scanf("%lld",&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;//记录a[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);//右端点为2或-2中靠右的一个
endpos=n+1;
if(startpos!=0x3f3f3f3f&&startpos<endpos){
ans+=endpos-startpos;
}
startpos=max(maxpos1,minpos1);//右端点为1或-1中靠右的一个
endpos=min(min(maxpos2,minpos2),(int)n+1);//不能取到2或-2,所以取最小值
if(startpos!=0x3f3f3f3f&&startpos<endpos){
ans+=endpos-startpos;
}
}//情况二:正负数都存在
printf("%lld\n",ans);
return 0;
}
(4)第四题:最小方差(variance)
情况:赛中0分,已补题
题意:有一颗无根树,已知包含n个点,n-1条边,且边权全部为1,请在数中寻找一个树根 ,当树根确定后计算出树上每个点到根的距离,得到一个长度为n的序列a。请让序列a的方差最小。
赛时本题做题想法:无
题解:二次扫描法,先进性式子推导,将平均值x去掉,避免小数double丢失精度,再进行两次搜索
1.preview:
2.dfs1:
sum1[u]:以u为根的子树中,每个点到u的距离的和
sum[2]:以u为根的子树中,每个点到u的距离的平方的和
2.dfs2:
//与dfs1是相似的,二次扫描
void dfs2(int u,int f,unsigned long long s1,unsigned long long 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;
}
unsigned long long ret1=sum1[u]-(sum1[v]+sz[v])+s1,ret2=sum2[u]-(sum2[v]+2*sum1[v]+sz[v])+s2,szu=n-sz[v];
dfs2(v,u,ret1+szu,ret2+2*ret1+szu);
}
return;
}
AC 代码:
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
unsigned long long sum2[100010],sum1[100010],sz[100010],n,res;
vector<int> G[100010];
void dfs1(int u,int f){
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v==f){
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]+=1;
return;
}
void dfs2(int u,int f,unsigned long long s1,unsigned long long 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;
}
unsigned long long ret1=sum1[u]-(sum1[v]+sz[v])+s1,ret2=sum2[u]-(sum2[v]+2*sum1[v]+sz[v])+s2,szu=n-sz[v];
dfs2(v,u,ret1+szu,ret2+2*ret1+szu);
}
return;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%lld",&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;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
res=1e18;
dfs1(1,0);
dfs2(1,0,0,0);
printf("%lld\n",res);
}
return 0;
}
4. 赛后总结:
本次比赛出现了时间分配不均的问题,应多加进行训练,加强自己的代码编写速度,防止有题目无法仔细思考