文章目录
S07547
学号:S07547
题目报告
总分130。
第一题AC。
第二题拿到三十分
第三题爆零
第四题需练习
赛中概况
第一题很快就做出来,暴力匹配即可
第二题没有推出普遍规律导致仅拿到三十分
第三题样例过了但是显然处理没到位直接爆炸
第四题对图知识点不掌握
解题报告
T1重复判断(repeat)
题目情况
比赛时AC
题目大意
判断一个字符串 a a a,是否由另一个字符串 b b b生成出来的,所谓的生成,其实就是把字符串 b b b重复若干次,即:判断字符串 a a a是否是字符串 b b b重复若干次得到的。
题目解析
直接暴力匹配即可,如果不相同直接跳出输出 N O NO NO。
AC代码
#include<iostream>
#include<iomanip>
#include<string>
#include<cstring>
using namespace std;
string a,b;
int main(){
//freopen("repeat.in","r",stdin);
//freopen("repeat.out","w",stdout);
int n;
scanf("%d",&n);
while(n--){
cin>>a>>b;
bool f=0;
if(a.size()<b.size()){
cout<<"NO\n";
continue;
}
for(int i=0,j=0;i<a.size();i++,j++){
if(j==b.size()) j=0;
if(a[i]!=b[j]){
cout<<"NO\n";
f=1;
break;
}
}
if(f==0){
cout<<"YES\n";
}
}
return 0;
}
T2歪果仁学英语(multiplication)
题目情况
比赛时没考虑到其他情况导致没找到普遍规律,影响到解题思路导致只拿到三十分
题目大意
歪果仁不太会乘法,原因是他们被不过九九乘法表,小可听到之后,提出了一种不需要九九乘法表也可以计算乘法的方式,对于a × b:
- 将a,b的每一位上的数码画成线,不同位之间分隔开。
- a 和 b 的方向垂直画出。
- 数出每个方向上交点的个数,即是 c 对应位置上的数码。
样例图给出了计算12 ×13的方法: - 红色线分别画出 1 条和 2 条;
- 蓝色线分别画出 1 条和 3 条;
- 数出红、蓝色线的交点个数,依次为 1,5,6 个;
- 得到答案:12 × 13 = 156。
给出两个数字 a, b,求它们的乘积时交点的总个数是多少。
题目解析
错误的思路是将
a
∗
b
a*b
a∗b的积按位相加。(只能对部分样例)
正确的思路应该是将
a
a
a的每位加起来和
b
b
b的每位加起来,最后算出和的乘积即可
AC代码
#include<iostream>
#include<iomanip>
#include<string>
#include<cstring>
using namespace std;
int a,b,sua,sub;
int main(){
//freopen("multiplication.in","r",stdin);
//freopen("multiplication.out","w",stdout);
scanf("%d%d",&a,&b);
while(a){
sua+=a%10;
a=a/10;
}
while(b){
sub+=b%10;
b=b/10;
}
printf("%d",sua*sub);
}
T3去重求和(summation)
题目情况
G G GG GG
题目大意
小可有一个长度为 n 的序列 。
他定义
s
u
m
(
l
,
r
)
sum(l,r)
sum(l,r),为
a
[
l
]
a[l]
a[l] ~
a
[
r
]
a[r]
a[r] ,这些数去重之后的和。
请求出
∑
l
=
1
n
∑
r
=
1
n
s
u
m
(
l
,
r
)
\sum_{l=1}^n\sum_{r=1}^nsum(l,r)
l=1∑nr=1∑nsum(l,r)
例如
输入
4
1 1 4 5
即为
s
u
m
(
1
,
1
)
=
1
,
s
u
m
(
1
,
2
)
=
1
,
s
u
m
(
1
,
3
)
=
5
,
s
u
m
(
1
,
4
)
=
10
s
u
m
(
2
,
2
)
=
1
,
s
u
m
(
2
,
3
)
=
5
,
s
u
m
(
2
,
4
)
=
10
s
u
m
(
3
,
3
)
=
4
,
s
u
m
(
3
,
4
)
=
9
s
u
m
(
1
,
2
)
=
5
\begin{aligned} & sum(1,1)=1,sum(1,2)=1,sum(1,3)=5,sum(1,4)=10\\ & sum(2,2)=1,sum(2,3)=5,sum(2,4)=10\\ & sum(3,3)=4,sum(3,4)=9\\ & sum(1,2)=5\\ \end{aligned}
sum(1,1)=1,sum(1,2)=1,sum(1,3)=5,sum(1,4)=10sum(2,2)=1,sum(2,3)=5,sum(2,4)=10sum(3,3)=4,sum(3,4)=9sum(1,2)=5
因此答案为1+1+5+10+1+5+10+4+9+5=51。
题目解析
50分:过前10个 的样例,思路就可以枚举所有的
[
l
,
r
]
[l,r]
[l,r] 。
20分:过4个不重复样例,可以看每个元素贡献多少次,找规律
a
i
a_i
ai。
互不相同,那么就有
s
u
m
(
l
,
r
)
=
∑
i
=
1
r
a
i
sum(l,r)=\sum_{i=1}^ra_i
sum(l,r)=i=1∑rai
考虑枚举每个
a
i
a_i
ai 算贡献,那么答案就是 :
∑
i
=
1
n
a
i
∗
(
n
−
i
+
1
)
∗
i
\sum_{i=1}^na_i*(n-i+1)*i
∑i=1nai∗(n−i+1)∗i。
这是因为对于每个
a
i
a_i
ai,满足
1
≤
l
≤
i
≤
r
≤
n
1 \leq l \leq i \leq r\leq n
1≤l≤i≤r≤n 的区间
[
l
,
r
]
[l,r]
[l,r] 会将其计入贡献。
而这样的区间共有
i
∗
(
n
−
i
+
1
)
i*(n-i+1)
i∗(n−i+1)个,因此就得到了上面的式子。
时间复杂度
O
(
n
)
O(n)
O(n),可以过
n
≤
500000
n\leq500000
n≤500000问题规模。
100分:
去重后怎么做呢?考虑换一种方式解释去重:我们只计算每个数第一次出现时产生的贡献。设
p
i
p_i
pi为最大的
j
<
i
j\lt i
j<i使得
a
j
=
a
i
a_j=a_i
aj=ai(不存在则为0),即
p
i
=
m
a
x
p_i=max
pi=max{
j
∣
j
<
i
,
a
j
=
a
i
j|j\lt i,a_j=a_i
j∣j<i,aj=ai}。
那么在这种方式下,
a
i
a_i
ai 在答案中的系数就是
(
i
−
p
i
)
(
n
−
i
+
1
)
(i-p_i)(n-i+1)
(i−pi)(n−i+1)。
计算
p
i
p_i
pi可以使用 hash 或者 map 做到
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),那么算法的时间复杂度就是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
AC代码
#include<iostream>
#include<map>
#define ll long long
using namespace std;
map<ll,ll> v;
ll ans,n,a[500005];
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&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;
}
T4点集操作(point)
题目情况
G G GG GG
题目大意
小可在学习图论,他有一个有向无环图,他想知道对这个图做任意次 m o d i f y ( i , j ) modify(i,j) modify(i,j)操作之后的图中剩余的最小点数,其中 1 ≤ i , j ≤ n 1\leq i,j\leq n 1≤i,j≤n,其中 m o d i f y ( i , j ) modify(i,j) modify(i,j)为一次操作:
1.任选不同的两个点
i
,
j
i,j
i,j
2.称
A
i
A_i
Ai为
i
i
i能到达的所有点组成的点集,
A
j
A_j
Aj为
j
j
j 能到达的所有点组成的点集 。(注意:每个点可以到达的点集包含这个点本身)
3.设 B 为一个最大的点集,满足 B 既是
A
i
A_i
Ai的子集,又是
A
j
A_j
Aj的子集 。
4.将 B 在图中变成一个新点,B 内的所有边全部删除。点集 B 以外的点与点集 B 以内的点的连边关系转移到新点上。
题目解析
可以使用模拟暴力拿到一些分10分左右,就是模拟题目说的modify过程。
正解还是较思维。
100分正解:
样例画图可得,把有入度的相关点的重叠点,变成一个大点B,是一次modify操作。
整体把1、4、5点,合成一个大点,最终变成3个点。
要是把5 4边去掉,就没有融合的大点,最小点数,仍然保持5个点。
发现在一个单向链上,只需要让链头的两个点进行一次操作就可以将整个链变成两个点。
所以每次操作可以在一条所含点数超过 2 的单向链上进行,直至不能继续操作,剩下的点的个数即为图中剩余的最小点数。
由上面操作的特殊性可以知道所有入度为 0 的点删不掉,所有满足所有入边对应点入度为 0 的点也删不掉,可以将这些点(所有满足所有入边对应点入度为 0 的点)看成新点。统计这些点个数即可。
复杂度:
O
(
n
+
m
)
O(n+m)
O(n+m)
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,x[N<<1],y[N<<1],in[N];
int h[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=h[u];
h[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=h[v[i]];j;j=e[j].next){//第二层
if(!b[e[j].to]){//若第二层的点的所有入边对应点入度等于零(有些点既在第二层又在其他层
b[e[j].to]=1;
ans++;//这些点虽然会被删除,但是他们可以充当新点,相当于保留
}
}
}
cout<<ans;
return 0;
}
赛后总结
勤加联系,多推规律