~模拟赛5~

目录

一.重复判断

二.歪果仁学乘法

三.去重求和

四.点集操作


一.重复判断

1.题目描述

判断一个字符串a,是否由另一个字符串b重复若干次得到的。

2.思路

(1)用find函数在a中循环查找b,找到后标记为“*”,结束后若数组中全是“*”那么a就是是否由另一个字b重复得到的。

(2) j表示当前匹配到b字符串的第j位。i表示当前匹配到a字符串的第i位,两两比较,如果t等于t.size(),说明一遍已经复制完了,从零开始重新匹配。

下面是思路(1)的                                                                                                                                           

3.AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
	string a,b;
	long long al=0,bl=0,te=0,t;
	cin>>t;
	while(t--)
	{
		cin>>a>>b;
		al=a.size(),bl=b.size(),te=0;
		while(1)
		{
			te=a.find(b);
			if(te==-1)
			{
				break;
			}
			for(int i=0;i<bl;i++)
			{
				a[te+i]='*';
			}
		}
		te=0;
		for(int i=0;i<al;i++)
		{
			if(a[i]!='*')
			{
				te=1;
				cout<<"NO"<<endl;
				break;
			}
		}
		if(te==0)
		{
			cout<<"YES"<<endl;
		}	
	} 
    return 0;
	
}

二.歪果仁学乘法

1.题目描述

歪果仁不太会乘法,原因是他们被不过九九乘法表,对于a × b:

1.将a,b的每一位上的数码画成线,不同位之间分隔开。
2.a 和 b 的方向垂直画出。
3.数出每个方向上交点的个数,即是 c 对应位置上的数码。

给出两个数字 a, b,求它们的乘积时交点的总个数是多少

2.思路

找规律:因为0\leq a,b\leq 99所以将a、b拆分成十位和个位两个数字,然后两两求交点数即可。

注意:不能算出来以后再求各数位和,有的数据不是这个规律

3.AC代码

#include<bits/stdc++.h>
using namespace std;
int hhh(int a, int b)
{
	int a1=a/10,a2=a%10,b1=b/10,b2=b%10;
	return a1*b1+a1*b2+a2*b1+a2*b2;
}
int main()
{
	int a,b;
	cin>>a>>b;
	cout<<hhh(a,b);
	return 0;
}

三.去重求和

1.题目描述

定义sum(l,r)sum(l,r),为a[l]a[l] ~ a[r]a[r] ,这些数去重之后的和。

请求出

 2.思路

我们只计算每个数第一次出现时产生的贡献。设pi为最大的j<i使得 aj=ai(不存在则为0),
那么在这种方式下, 在答案中的系数就是(i-pi)(n-i+1)
计算 可以使用 map(用桶会爆)做到 ,那么算法的时间复杂度就是O(nlogn)

3.AC代码

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
typedef long long ll;
const int mod = 1e9 + 7;
int n,a[N];
map<int,int>mp;
int l[N],r[N];
int main()
{
	cin>>n;
	for(int i=1;i<=n;++i)
	{
		cin >> a[i];
		mp[a[i]] = 1;
	}
	mp.clear();
	for(int i = 1; i <= n; ++i)
	{
		l[i] = mp[a[i]] + 1;
		mp[a[i]] = i;
	}
	ll ans = 0;
	for(int i = 1; i <= n; ++i)
	{
		ans += 1ll * a[i] * (i - l[i] + 1) % mod * (n - i + 1) % mod;
		ans %= mod;
	}
	cout << ans << endl;
}

四.点集操作

一.题目描述

有一个有向无环图,对这个图做任意次modify(i,j)操作之后的图中剩余的最小点数,其中,其中modify(i,j)为一次操作:

1.任选不同的两个点i,j
2.称 A​i​​ 为 i 能到达的所有点组成的点集,A​j​​ 为 j能到达的所有点组成的点集 。(包含这个点本身)
3.设 B 为一个最大的点集,满足 B 既是 A​i​​ 的子集,又是A​j​​的子集 。
4.将 B 在图中变成一个新点,B 内的所有边全部删除。点集 B 以外的点与点集 B 以内的点的连边关系转移到新点上。

二.思路

样例可得,把有入度的相关点的重叠点,变成一个大点B,是一次modify操作。
整体把1、4、5点,合成一个大点,最终变成3个点。
要是把5 4边去掉,就没有融合的大点,最小点数,仍然保持5个点。
发现在一个单向链上,只需要让链头的两个点进行一次操作就可以将整个链变成两个点。
所以每次操作可以在一条所含点数超过 2 的单向链上进行,直至不能继续操作,剩下的点的个数即为图中剩余的最小点数
由上面操作的特殊性可以知道所有入度为 0 的点删不掉,所有满足所有入边对应点入度为 0 的点也删不掉,可以将这些点(满足所有入边对应点入度为 0 的点)看成新点。统计这些点个数即可。
复杂度:O(n+m)

三.AC代码

#include<bits/stdc++.h>
using namespace std;
const int mn=1e6+5;
int n,m,x[mn*2],y[mn*2],in[mn];
int head[mn],num=0,ans;
bool b[mn],vis[mn];
vector<int>v;
struct hhh
{
	int to,next;
}e[mn*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;
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~THE END~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值