1.比赛概况:
比赛共4题,得了140/400,T1-T4得分70/20/50/0
2.比赛过程:
今天的比赛,比昨天难了一(亿)点,但我得分竟然比昨天高!
T1一看题就有了思路,直接写了,(结果是个分类讨论,真服了!)
T2一看题就把从m到n遍历排除(结果这竟然是正解!),又想把k(≤100)分离,跑一个搜索,加上亿点剪枝,还写了个对拍,确定对了(结果TLE了,还没别人暴力的分多 QwQ~~)
T3一看题,有印象!是个贪心。一写,懵了。赶紧画图,又画懵了。最后写了一个暴力。(结果思路没错,只是我把两个数组和一起,没法用了)
T4很难。脑子里只有一个背包模板,使劲往上套,最后写了个奇怪的东西。
3.题解报告:
T1:人员借调 (transfer.cpp)
情况: 70分,已补题。
题意:小可处理n件事情,处理第 i件的耗时为 ai 分钟。正常的过程为小可从 A 地到 B 地进行处理,处理结束之后回到 A 地。但是如果小可在 B 地待连续大于等于240分钟时,会强制把小可留在 A 地 7 天( 10080 分钟),再继续工作。小可有一个对策,在 240 分钟快到的时候就此 B 地回到 A 地,然后再去 B 地,这样的话 240分钟就会重新计时。注意:往返一次耗时 400 分钟(不计算待在 B 地的时间)。现在小可从 A 地准备出发,需要在 B 地处理完所有事,然后回到 A 地正常的工作。注意:事情不可以打乱,并且处理事情必须处理完才可以继续后面的内容。请问小可至少需要多少分钟?
数据范围:
在 20% 数据下:n=1
在 40% 数据下:1≤n≤2
另有 10% 数据:有 ai≥240
对于 100% 数据:有 1≤n,ai≤1000
赛时本题做题想法:把数据划为小于240的几组,再计算时间。如果有≥240的,与前面的合为一组,再让时间+10080.注意无论如何都有一次往返。
题解:有两种情况:1.处理事情且总时间不超过240,计算一次往返。2.一次性全部处理完。又分两种情况:(1)总时间超过240,计算一下滞留7天时间(2)计算去了B地之后,某一件事情办完会被留在A地7天,选择在B地全部办完所有事情。注意:前缀和优化。
AC代码:
#include<iostream>
#include<cstdio>
using namespace std;
int n,ans=0,p=0,x,cnt=0;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
ans+=x;
p+=x;
if(p>=240){
cnt++;
p=x;
}
}
if(cnt){
if(n==1) ans+=10080;
else ans+=min(10080,cnt*400);
}
cout<<ans+400;
return 0;
}
T2:计算(calc.cpp)
情况:20分,已补题。
题意:有三个整数 m,n,k.(设x为满足计算条件的数字):1:m≤x≤n 2:x在十进制下所有位上的数字之和为k。 x会很多,输出十进制下所有位上的数字的积最大的x(如果有多个积相等,输出最小的x)保证有解。
数据范围:
在 30% 数据下:n≤1000,n−m≤1000
另有20%数据:n≤2×105,n−m=0
另有20%数据:n≤2×10^5,n−m≤2×10^5
在100%数据下:1≤m≤n≤5×10^6,1≤k≤100,1≤T≤100
赛时本题做题想法:先把从m到n遍历排除,然后搜索,把k分成a份(a≤7)(0≤每一份≤9),再进行剪枝。
题解:空间换时间,打表处理1~5*10^6的各个数位的和与积。再从m到n遍历。
AC代码:
#include<iostream>
#include<cstdio>
using namespace std;
int a[5000005],b[5000005]={1},t,n,m,k;
int main(){
for(int i=1;i<=5000000;i++){
a[i]=a[i/10]+(i%10);
b[i]=b[i/10]*(i%10);
}
b[0]=-1;
cin>>t;
while(t--){
cin>>m>>n>>k;
int ans=0;
for(int i=m;i<=n;i++){
if(a[i]==k&&b[i]>b[ans]) ans=i;
}
cout<<ans<<" "<<b[ans]<<endl;
}
return 0;
}
T3:智能公交(transit.cpp)
情况:50分,已补题。
题意:共有n个公交站台(1,2,⋯,n)有一辆智能公交车会在这 n个站台之间穿梭。如果智能公交上没有乘客,会停靠在x站台。有人按动公交站台上的按钮,智能公交就会快到到达相应的站台,随后智能公交把人送到目的地,又回到x站台。现在有m个人要依次乘坐智能公交,每个人都会等待智能公交停在x站台之后在按动当前站台按钮准备乘坐公交。现在已知第i个人都是从a站台到b站台。计算x使得智能公交移动距离最短。最终输出x和最短的距离x若有多个,输出最小的一个。
数据范围
对于10%数据:1≤n,m≤100
对于50%数据:1≤n,m≤2000
另有20%数据:对于任意的i(1≤i<m) 有ai<bi<ai+1<bi+1
对于100%数据:1≤n,m≤5×10^5,1≤a,b≤n
赛时本题做题想法:用贪心来判断每一个站台公交要跑的最短距离,利用差分来维护左右两侧公交起始与终点的数量。结果,想法很好,写不出来。最后打了个暴力。
题解:有点像个数学题。公交车从站台a到站台b,且停靠位置x在a到b之间,那么移动距离为:|x-a|+|a-b|+|x-b|=2|a-b| 。在这之外,每向外一个站台公交车多移动的距离就+2(因为往返*2),公交车多移动的距离呈公差为2的等差数列。假设f(x)表示公交车停靠在 的总移动距离,那么给定a,b的时候,相当于将整个数组全部加2|a-b|,并且将a-1到1的位置额外加一个公差为 2 的等差数列;将b+1到n 的位置加一个公差为2的等差数列。所以用差分、前缀和来维护区间加法和等差数列加法。
AC代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#define ll long long
#define N 500005
using namespace std;
ll n,m,a,b,ans=0,l[N],r[N],sl[N],sr[N],sum=0x3f3f3f3f3f3f3f3f,p;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
l[a-1]+=2;
r[b+1]+=2;
ans+=2*(b-a);
}
for(int i=n;i>=1;i--){
l[i]+=l[i+1];
sl[i]=sl[i+1]+l[i];
}
for(int i=1;i<=n;i++){
r[i]+=r[i-1];
sr[i]=sr[i-1]+r[i];
if(sl[i]+sr[i]<sum){
sum=sl[i]+sr[i];
p=i;
}
}
cout<<p<<" "<<ans+sum;
return 0;
}
T4:异或和(exclusive.cpp)
情况:0分,已补交。
题意:总共有n个数字,每个数字的大小ai和属于某个集合 bi。在一个集合中选择一个数字,收益为这个数字的大小,选择多个数字,收益为这些数字的异或和。总收益为每个集合的收益之和。注意:最多从中选择m(m≤n)个数字,使这些数字总收益最大。
数据范围:
有10%数据:m=n,bi=1,1≤n,ai≤2000
另有10%数据:1≤bi≤2,1≤n,ai≤2000
另有30%数据:1≤bi≤2000,1≤n,ai≤200
在100%数据下:1≤bi≤2000,1≤n,ai≤2000
赛时本题做题想法:是个分组背包。然后……就没有然后了。这题的暴力我都写不出来,真服了!(但老师说这题是入门的极限了,基本不会考) (我第一次见到分组背包+3维压2维+答案给我都看不懂)。
题解:略(无法用语言描述)
这题要对每一组进行预处理dp[i][j]表示某一组前i个数,凑成异或和为j的数的最少个数。状态转移方程为
因为数据≤2000,所以我们三维压二维,使用滚动数组,把每一组的结果存在sum中。sum[i][j]表示第i组选j个数最大的异或值.注意:要初始化,可能被卡住。
最后使用分组背包得到最大异或值。
AC代码:
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
int n,m,a,b,t,dp[2005][2050],num[2005][2005],dpp[2050],z[2005];
vector<int> v[2005];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%d%d",&a,&b);
v[b].push_back(a);
z[b]++;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=2047;j++) dp[i][j]=1e9;
}
for(int zu=1;zu<=2000;zu++){
if(z[zu]!=0) dp[1][v[zu][0]]=1;
for(int i=2;i<=z[zu];i++){
dp[i][v[zu][i-1]]=1;
for(int j=1;j<=2047;j++){
if(dp[i-1][j]!=1e9){
dp[i][j]=min(dp[i][j],dp[i-1][j]);
dp[i][j^v[zu][i-1]]=min(dp[i][j^v[zu][i-1]],dp[i-1][j]+1);
}
}
}
for(int j=1;j<2047;j++){
if(dp[z[zu]][j]!=1e9) num[zu][dp[z[zu]][j]]=j;
}
for(int i=1;i<=z[zu];i++){
for(int j=1;j<2047;j++) dp[i][j]=1e9;
}
}
for(int i=1;i<=2000;i++){
for(int j=m;j>=1;j--){
for(int k=1;k<=z[i];k++){
if(j>=k) dpp[j]=max(dpp[j],dpp[j-k]+num[i][k]);
}
}
}
cout<<dpp[m];
return 0;
}
4. 赛后总结:
今天题很难(QwQ),做成这样不容易。但要注意几个问题:1.思路放宽一些(T2),多考虑一些情况(T1),(不然像我T1少了一部分,T2首先排除了正解) 2.注意细节,把思路理清楚(T3) 3.
最后,希望明天简单一些。orz。
日期:2023年10月01日星期天
姓名:董峻熙