集训第五天补题报告


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:

  1. 将a,b的每一位上的数码画成线,不同位之间分隔开。
  2. a 和 b 的方向垂直画出。
  3. 数出每个方向上交点的个数,即是 c 对应位置上的数码。
    样例图给出了计算12 ×13的方法:
  4. 红色线分别画出 1 条和 2 条;
  5. 蓝色线分别画出 1 条和 3 条;
  6. 数出红、蓝色线的交点个数,依次为 1,5,6 个;
  7. 得到答案:12 × 13 = 156。
    给出两个数字 a, b,求它们的乘积时交点的总个数是多少。
题目解析

错误的思路是将 a ∗ b a*b ab的积按位相加。(只能对部分样例)
正确的思路应该是将 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=1nr=1nsum(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=1rai
考虑枚举每个 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(ni+1)i
这是因为对于每个 a i a_i ai,满足 1 ≤ l ≤ i ≤ r ≤ n 1 \leq l \leq i \leq r\leq n 1lirn 的区间 [ l , r ] [l,r] [l,r] 会将其计入贡献。
而这样的区间共有 i ∗ ( n − i + 1 ) i*(n-i+1) i(ni+1)个,因此就得到了上面的式子。
时间复杂度 O ( n ) O(n) O(n),可以过 n ≤ 500000 n\leq500000 n500000问题规模。
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 jj<i,aj=ai}。
那么在这种方式下, a i a_i ai 在答案中的系数就是 ( i − p i ) ( n − i + 1 ) (i-p_i)(n-i+1) (ipi)(ni+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) modifyij操作之后的图中剩余的最小点数,其中 1 ≤ i , j ≤ n 1\leq i,j\leq n 1i,jn,其中 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; 
}

赛后总结

勤加联系,多推规律

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值