牛客练习赛51-记录

正题

比赛链接:https://ac.nowcoder.com/acm/contest/1083#question


成绩

在这里插入图片描述
可怜的 z y c T 3 zycT3 zycT3 n = 0 n=0 n=0卡了半天,这里感谢一下排雷


总结

比赛状态较好,后面没有 T 6 T6 T6的题解


T 1 : a b c T1:abc T1:abc

题目大意

给出一个字符串,求有多少个 a b c abc abc子序列

解题思路

用三个数组分别表示 a a a的个数, a b ab ab的个数, a b c abc abc的个数即可

c o d e code code

#include
#include
#include
using namespace std;
char s[110000];
long long n,a,b,c;
int main()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=1;i<=n;i++){
		if(s[i]=='a') a++;
		if(s[i]=='b') b+=a;
		if(s[i]=='c') c+=b;
	}
	printf("%lld",c);
}

T 2 : T2: T2: 子串查询

题目大意

给出一个字符串, q q q个询问,每次询问一个字符串求它是否是前面那个字符串的子序列。

解题思路

a i , j a_{i,j} ai,j表示第 i i i个开始 j j j字符最早出现在哪里,然后一个一个跳就好了
时间复杂度 O ( 26 n + ∑ ∣ q ∣ ) O(26n+\sum |q|) O(26n+q)

c o d e code code

#include
#include
#include
using namespace std;
const int N=1e5+100;
int n,t,a[N][26];
char s[N],q[60];
int main()
{
	scanf("%d%d",&n,&t);
	scanf("%s",s+1);
	memset(a,127/3,sizeof(a));
	for(int i=n;i>=1;i--){
		for(int j=0;j<26;j++)
			a[i][j]=a[i+1][j];
		a[i][s[i]-'a']=i;
	}
	while(t--){
		scanf("%s",q+1);
		int m=strlen(q+1),now=1,flag=0;
		for(int i=1;i<=m;i++){
			now=a[now][q[i]-'a']+1;
			if(now>n+1){flag=1;break;}
		}
		if(flag) printf("NO\n");
		else printf("YES\n");
	} 
}

T 3 : T3: T3:勾股定理

题目大意

给一个正整数 n n n,求两个正整数 a , b a,b a,b可以和 n n n组成勾股数。

解题思路

先考虑若 n n n为奇数我们有 a 2 − b 2 = n 2 a^2-b^2=n^2 a2b2=n2
( a + b ) ( a − b ) = n 2 (a+b)(a-b)=n^2 (a+b)(ab)=n2
a = b + 1 a=b+1 a=b+1那么有 ( b + 1 + b ) ( b + 1 − b ) = n 2 (b+1+b)(b+1-b)=n^2 (b+1+b)(b+1b)=n2
2 b + 1 = n 2 2b+1=n^2 2b+1=n2
那么当 n n n为奇数时都有解。
那我们看偶数若 a 2 − b 2 = n 2 a^2-b^2=n^2 a2b2=n2我们有 ( 2 a ) 2 − ( 2 b ) 2 = ( 2 n ) 2 (2a)^2-(2b)^2=(2n)^2 (2a)2(2b)2=(2n)2
那么我们可以每次将 n n n 2 2 2到奇数为止然后计算出 a , b a,b a,b再乘回去。
但是特殊的是当 n = 2 k n=2^k n=2k时我们将 n n n除到 4 4 4然后用 3 , 5 3,5 3,5来进行匹配即可。

c o d e code code

#include
#include
#include
#include
#define ll long long
using namespace std;
ll n,a,b,z;
int main()
{
	scanf("%lld",&n);
	if(n<=2) return printf("-1")&1; 
	if((n*n)&1) printf("%lld %lld\n",n*n/2,n*n/2+1);
	else{
		while(!(n&1)&&n>4){
			n=n/2;
			z++;
		}
		a=n*n/2;b=n*n/2+1;
		if(n==4) a=3,b=5;
		while(z--)
			a*=2,b*=2;
		printf("%lld %lld\n",a,b);
	}
}

T 4 : T4: T4:羊吃草

题目大意

若干个区间,每次询问一段区间,求这段区间内每个点匹配一个区间最多能匹配到多少个。

解题思路

就是区间配点的问题,和jzoj6274-[NOIP提高组模拟1]梦境【贪心,堆】这题一样,这里不过多称述

c o d e code code

#include
#include
#include
#include 
using namespace std;
const int N=410;
priority_queue<int> q;
struct node{
	int l,r;
}a[N];
int L,R,n,m;
bool cMp(node x,node y)
{return x.l<y.l;}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i].l);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i].r);
	sort(a+1,a+1+n,cMp);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&L,&R);
		int tail=1,ans=0;
		while(!q.empty()) q.pop();
		for(int j=L;j<=R;j++){
			while(tail<=n&&a[tail].l<=j)
				q.push(-a[tail].r),tail++;
			while(!q.empty()&&-q.top()<j)
				q.pop();
			if(!q.empty()) ans++,q.pop();
		}
		printf("%d\n",ans);
	}
}

T 5 : T5: T5:数列

题目大意

将一个每个值都非0的序列,求一个序列使得 a i = a i − 1 + 1 ( i > 1 ) a_i=a_{i-1}+1(i>1) ai=ai1+1(i>1)的情况最多

解题思路

我们可以将这个数列分成若干段连续铺满的区间,然后每段长度为 l l l的区间贡献为 l − 1 l-1 l1,然后价格为 ( l + 1 ) ∗ l 2 \frac{(l+1)*l}{2} 2(l+1)l
我们考虑贪心,因为一段长度为 l l l的区间贡献为 l − 1 l-1 l1,我们可以视为第一个没有贡献,然后我们假设已经知道了要分成 k k k段,然后我们让序列长度提前减去 k k k那么这样一段长度为 l l l的区间贡献就是 l l l了,那我们只需要价值最少就好了,这个我们可以均摊即可。

然后 k k k我们进行枚举,时间复杂度 O ( n ) O(n) O(n)

c o d e code code

#include
#include
#include
using namespace std;
int n,m,N;
int main()
{
	scanf("%d%d",&n,&m);
	N=n;
	for(int i=1;i<=n;i++){
		int k=n/i,cost=k*(k+1)/2*i+n%i*(k+1);
		if(cost<=m){
			int z=0,s=1;
			while(z<n){
				for(int j=1;j<=k+(s<=(n%i));j++)
					printf("%d ",j),z++;
				s++;
			}
			break;
		}
	}
}

你可能感兴趣的:(数论and数学,贪心,字符串)