日期:2023年10月5日
学号:S07246
姓名:江守栋
目录
· 比赛概况
总分 : 230 / 400
T1 | T2 | T3 | T4 |
AC | AC | 0分 | 30分 |
今天的 T4 我的思路是正确的,但是数据量炸掉了,扣了 70 分
· 比赛过程
前两题水题,15分钟秒了
看了眼第三题,感觉需要推很久,就先没做
第四题是一道图论,昨天晚上做了很久邻接表的题,现在掌握得比较熟练了
赛时在纸上画了一堆图,推了很久,找到了思路,
可惜我写了两层循环,在平台里提交过了,但是判题机运行时间超限了,丢了 70 分 。。。
· 题目分析
T1 【重复判断 repeat】
1、题目大意
判断字符串a是否是字符串b重复若干次得到的。
2、比赛中的思考
是道水题,模拟秒了
3、解题思路
两个指针同时分别遍历字符串 和 字符串 ,
因为 的长度短,可能会多次遍历,所以需要每次 ++ 后 % ,保证下标不会越界
如果 != 则标记为 false ,如果遍历结束了,而 j !=0 说明 a 中出现了多余元素,不是重复字符串。
4、AC代码
#include<bits/stdc++.h>
using namespace std;
int T;
string s,s1;
int main()
{
freopen("repeat.in","r",stdin);
freopen("repeat.out","w",stdout);
cin>>T;
while(T--){
bool flag=1;
cin>>s>>s1;
int j=0;
for(int i=0;i<s.size();i++){
if(s[i]!=s1[j]){
flag=0;
break;
}
j++;
j%=s1.size();
}
if(flag==1&&j==0){
cout<<"YES";
}
else{
cout<<"NO";
}
cout<<endl;
}
fclose(stdin);
fclose(stdout);
return 0;
}
T2【歪果仁学乘法 multiplication】
1、题目大意
现有一种不需要九九乘法表也可以计算乘法的方式,对于a × b:
- 将a,b的每一位上的数码画成线,不同位之间分隔开。
- a 和 b 的方向垂直画出。
- 数出每个方向上交点的个数,即是 c 对应位置上的数码。
样例图给出了计算12 ×13的方法:
1.红色线分别画出 1 条和 2 条;
2.蓝色线分别画出 1 条和 3 条;
3.数出红、蓝色线的交点个数,依次为 1,5,6 个;
4.得到答案:12 × 13 = 156。
给出两个数字 a, b,求它们的乘积时交点的总个数是多少
2、比赛中的思考
仍然是一道水题,俩循环秒了
3、解题思路
根据图例即可发现答案等于两个因数按位相乘后相加,注意不要理解成将乘积数位分离,
这样会漏掉计算中进位的交叉点。
只需两个 将 和 数位分离并相加,最后将得到的两个值相乘即可
4、AC代码
#include<bits/stdc++.h>
using namespace std;
int a,b;
int main()
{
freopen("multiplication.in","r",stdin);
freopen("multiplication.out","w",stdout);
cin>>a>>b;
int sum1=0,sum2=0;
while(a!=0){
sum1+=a%10;
a/=10;
}
while(b!=0){
sum2+=b%10;
b/=10;
}
cout<<sum1*sum2;
fclose(stdin);
fclose(stdout);
return 0;
}
T3【去重求和 summation】
1、题目大意
定义 为 ~ 间的数字去重后的和。
求
2、比赛中的思考
不会,而且第四题耗费太多时间,这道题没有思路,只能输出样例
3、解题思路
只计算每个数第一次出现时对答案的贡献
如果第 个元素前面(以左端点 为起点)没有重复的,则它在当前以 为左端点的所有 中出现次数为
如果第 个元素前面(以左端点 为起点)没有重复的,则它在当前以 为左端点的所有 中出现次数为 0
第 个元素在所有的 中最多出现 次
如果第 个元素前面与 相同的元素的编号为 ,则第 个元素出现次数为
综上所述,可以得出,第 个元素的对答案的贡献为:
4、AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
ll a[N], n;
ll ans = 0;
map<ll, ll> p;
int main() {
freopen("summation.in", "r", stdin);
freopen("summation.out", "w", stdout);
scanf("%lld", &n);
for (ll i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (ll i = 1; i <= n; i++) {
ll x = p[a[i]];
ans = (ans + (n - i + 1) % mod * a[i] % mod * (i - x) % mod) % mod; //公式
p[a[i]] = i;
}
printf("%lld", ans % mod);
fclose(stdin);
fclose(stdout);
return 0;
}
T4【点集操作 point】
1、题目大意
一个有向无环图,对这个图做任意次 操作后图中剩余的最小点数
其中 的含义为:
1. 任选不同的两个点
2. 称 为 能到达的所有点组成的点集, 为 能到达的所有点组成的点集 。(注意:每个点可以到达的点集包含这个点本身)
3. 设 B 为一个最大的点集,满足 B 既是 的子集,又是 的子集 。
4. 将 B 在图中变成一个新点,B 内的所有边全部删除。点集 B 以外的点与点集 B 以内的点的连边关系转移到新点上。
2、比赛中的思考
根据昨天的比赛找到了自己的短板,昨天回家专门练了很久图论的题,也算是临阵磨枪了
我的思路是先用链式前向星存图,再输入时统计 与 的出度和入度,输入结束后开始暴搜
首先将 初始值赋为 ,初始默认不需要删除任何点,随后开始遍历每个结点,
如果该结点既有入度也有出度,则肯定要将这个结点与它的邻接点删除,循环将结点标记为删除
并将 意为删除掉了一个点,最后输出 即可 , 赛后在平台上提交了,显示AC
但是判题机狠狠地给了我一巴掌 , 跑出来之后时间超限,只得了30分
#include<bits/stdc++.h>
using namespace std;
int n,m,ver[2000005],head[2000005],nxt[2000005],tot=0;
bool vis[2000005];
int sum[2000005][5];
void add(int x,int y){
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
int main(){
freopen("point.in","r",stdin);
freopen("point.out","w",stdout);
memset(head,-1,sizeof head);
memset(vis,0,sizeof vis);
cin>>n>>m;
for(long long i=1;i<=m;i++){
int x,y;
cin>>x>>y;
add(x,y);
sum[x][2]++;
sum[y][1]++;
}
long long ans=n;
for(long long i=1;i<=n;i++){
if(vis[i]||(sum[i][1]>=1&&sum[i][2]>=1)){
for(long long j=head[i]; j!=-1;j=nxt[j]){
if(vis[ver[j]]==0){
ans--;
}
vis[ver[j]]=1;
}
}
}
cout<<ans;
fclose(stdin);
fclose(stdout);
return 0;
}
呜呜呜呜呜呜呜呜呜呜呜我的70分。。。
3、解题思路
首先将 置为 0
前向星存图,但是在输入时需要使用数组存储 和 ,而且只需要统计入度。
输出结束后判断一下 的入度 , 如果该节点的入度为零,则一定不需要删除,因为没有结点能够到达它,将其放入数组中,并将 ++
随后遍历所有的边,如果这条边的起点有入度,那么这条边指向的结点就能与起点一起合并
将这条边指向的点标记为合并
然后遍历数组 ,需要用两层循环,外层遍历 ,内层沿着以 为起点的链表遍历,如果遍历到的点的对应点入度为零,将这个点保留或把这个点下面的所属点合并成一个点,++
最后输出
4、AC代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5;
int n,m,x[MAXN*2],y[MAXN*2],in[MAXN];
int head[MAXN],num,ans;
bool b[MAXN],vis[MAXN];
vector<int>v;
struct node{
int to,next;
}e[MAXN*2];
void add(int u,int v){
e[++num].to=v;
e[num].next=head[u];
head[u]=num;
}
int main(){
cin >> n >> m;
for(int i=1;i<=m;++i){
cin >> 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++;
}
}
}
cout << ans;
return 0;
}____________________
· 赛后总结
今天的难度可以说是这五天中最简单的,前两道题也是一上来就直接秒掉了,但是第四题的图论卡了我很久,即使想了很久,最后扣掉了70分(死因:两层循环时间超限),300分直接变成270,以后做题要看准数据范围,对于可能超限的循环或变量内存,不要马马虎虎的提交上去,要想到优化的策略
第三题这种偏向数学思维的题目应该列表画图,找到其中的规律,只要把思路打开,想清楚题目,代码往往是非常好写的,以后也要注重于这方面的练习
比赛加油!rp++ ! ! !