日期: 2023 年 10 月 3 日星期二
学号:S07892
1. 比赛概况:
比赛总分共 4 题,满分 400,赛时拿到210分, 其中第一题100分, 第二题100分, 第三题10分, 第四题0分。
2. 比赛过程:
今天的题目比昨天较为简单,第一二题用了一个小时多一点就做完了,第一题直接暴力判断第二题用各位相加在相乘也能过,主要是三四题的困难较大,主要考验思维能力,做起来比较困难,没能有效编写
3. 题解报告:
1)第一题:重复判断((repeat)
情况:赛中100分。
题意:判断字符串a是否是字符串b重复若干次得到的,是输出"YES",不是输出"NO"。
赛时本题做题想法:先判断字符串a的长度是否是字符串b的倍数,如果不是那么a绝不是b重复的来的,再遍历字符串a,挨个字符判断是否与字符串b对应
题解:直接暴力for循环判断是否相等
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;
bool f;
int t,la,lb;
string a,b;
int main(){
scanf("%d",&t);
while(t--){
f=0;
cin>>a>>b;
la=a.size(),lb=b.size();
if(la%lb!=0){
printf("NO\n");
}
else{
for(int i=0;i<la;i++){
if(a[i]!=b[i%lb]){
f=1;
break;
}
}
if(f==1){
printf("NO\n");
}
else{
printf("YES\n");
}
}
}
return 0;
}
2)第二题:歪果仁学乘法(multiplication)
情况:赛中100分。
题意:给出两个数字 a, b,求它们的乘积时交点的总个数是多少。
赛时本题做题想法:将数字各位相加后等于线的数量,相乘就等于点的个数了
题解:另一种为两个数字按位相乘后相加
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;
int a,b,suma,sumb;
int main(){
scanf("%d%d",&a,&b);
while(a!=0){
suma=suma+(a%10);
a=a/10;
}
while(b!=0){
sumb=sumb+(b%10);
b=b/10;
}
printf("%d",suma*sumb);
return 0;
}
3)第三题:去重求和(summation)
情况:赛中10分,已补题。
题意:有个长度为n的序列a,sum(l,r)为a[l]~a[r]这些数去重之后的和。求出
赛时本题做题想法:以为是dp,便把dp[i][j]有那些情况得来的列出来,双层循环,认为能拿50分
题解:1.遇到这种开始没头绪的题,可以先从特殊情况入手总结了一个数字被加的次数就是左区间所能设的点加右区间所能设的点的规律。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;
map<ll,ll> v;
ll ans,n,a[500005];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
ans=(ans+((a[i]*(i-v[a[i]])%1000000007)*(n-i+1)%1000000007))%1000000007;
v[a[i]]=i;
}
printf("%lld",ans);
return 0;
}
4)第四题:点集操作(point)
情况:赛中0分,已补题。
题意:有一个有向无环图,求对这个图做任意次操作之后的图中剩余的最小点数,一次操作:
1. 任选不同的两个点
2. 称Ai为i能到达的所有点组成的点集,Aj为j能到达的所有点组成的点集 。(注意:每个点可
以到达的点集包含这个点本身)
3. 设B为一个最大的点集,满足B既是Ai的子集,又是Aj的子集 。
4. 将B在图中变成一个新点,B内的所有边全部删除。点集B以外的点与点集B以内的点的连边关
系转移到新点上。
赛时本题做题想法:链式前向星存图,模拟操作过程,将最小点数记录
题解:缩点操作,将链上的点能缩为两个,外层点不变,二层点后点缩至二层点,入度为零为外层点,不可能被缩成一点,被外层点所直接指向的点为二层点,其他点会缩到二层点,对最后的答案就没有影响了
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;
const int N=1e6+5;
int n,m,x[N<<1],y[N<<1],in[N],head[N],num,ans;
bool b[N],vis[N];
vector<int>v;
queue<int>q;
struct node{
int to,next;
}e[N<<1];
void add(int u,int v){
e[++num].to=v;
e[num].next=head[u];
head[u]=num;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
scanf("%d%d",&x[i],&y[i]);
add(x[i],y[i]);
in[y[i]]++;//统计入度
}
for(int i=1;i<=n;++i){
if(!in[i]){//如果入度为零
v.push_back(i);//记录入度为零的点 ,最外层的点
ans++;//这些点肯定不能被删去
}
}
for(int i=1;i<=m;++i){
if(in[x[i]]){
b[y[i]]=1;//将入边对应点入度大于零的点标记,这些点都不行
}
}
for(int i=0;i<v.size();++i){//没有入度的最外层
for(int j=head[v[i]];j;j=e[j].next){//第二层
if(!b[e[j].to]){//若第二层的点的所有入边对应点入度等于零(有些点既在第二层又在其它层)
b[e[j].to]=1;
ans++;//这些点虽然会被删除,但是他们可以充当新点,相当于保留
}
}
}
printf("%d",ans);
return 0;
}
4. 赛后总结:
本次比赛做前两道题快了一点,没出现了时间分配不均的问题,但后面两道题目思维考察比重较大,还是缺乏思维深度,做题太少,还应多加训练进行锻炼思维深度,加强这方面的能力