【题解】牛客练习赛51

比赛链接

A abc

前缀a的数量,后缀c的数量,遇到b就计算一次答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+100;
char s[N];
int cnt[N];
int main()
{
    ll ans= 0 ;
    scanf("%s",s+1);
    int n = strlen(s+1);
    for(int i=n;i>=1;i--){
        if(s[i]=='c') cnt[i] = cnt[i+1] +1;
        else cnt[i] = cnt[i+1];
    }
    int tmp =0 ;
    for(int i=1;i<=n;++i){
        if(s[i]=='a') tmp++;
        else if(s[i]=='b') ans+=1ll*tmp*cnt[i];
    }
    printf("%lld\n",ans);
}

 

B-子串查询

预处理出每个字符后面26个字母第一次出现的位置。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+100;
char s[N];
int pos[26][N],cnt[26],cur[N][26];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        pos[s[i]-'a'][++cnt[s[i]-'a']]=i;
    for(int i=0;i<=n;i++){
        for(int j=0;j<26;j++){
            int t = upper_bound(pos[j]+1,pos[j]+cnt[j]+1,i) - pos[j];
            if(t!=cnt[j]+1) cur[i][j]=pos[j][t];
        }
    }
    while(m--){
        char t[60];
        scanf("%s",t+1);
        int now = 0, len = strlen(t+1),flag=1;
        for(int i=1;i<=len;i++){
            if(cur[now][t[i]-'a']!=0) now = cur[now][t[i]-'a'];
            else {
                flag=0;
                break;
            }
        }
        puts(flag?"YES":"NO");
    }
}

 

C-勾股定理

这题比赛时一脸懵逼。周大佬说是以前打过的一道网络赛原题,好吧,忘记了。搜了一波题解。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
	ll n;
	cin>>n;
	if(n<=2) puts("-1");
	else {
		ll b,c;
		if(n % 2){
			b = (n*n-1)/2;
			c = (n*n+1)/2;
		}else {
			b = (n*n/2-2)/2;
			c = (n*n/2+2)/2;
		}
		printf("%lld %lld\n",c,b);
	}
 } 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
	ll n;
	cin>>n;
	if(n<=2) puts("-1");
	else {
		ll b,c;
		if(n%2==0){
			b = (n*n + 4)/4;
			c = b-2;
		}else{
			b = (n*n + 1)/2;
			c = b - 1;
		}
		printf("%lld %lld\n",c,b);
	}
 } 

 

D-羊吃草

解法一:

比赛时想的是贪心暴力。对于每一个位置记录能被哪些羊儿吃以及羊儿能吃到的左端点,然后对这些左端点进行从小到大排序。每次询问一个区间时,怎样给每个位置i分配羊儿呢?贪心选左端点最近的,假设现在有两个端点L1,L2,L1<L2,对于i到L1,以及i到L2,显然L2覆盖的区间大一些,那么L2或许就可以分配给其它L1到不了的位置,所以贪心选L1,并且标记L1。

#include<bits/stdc++.h>
using namespace std;
const int N = 400+10;
int vis[N];

struct node{
	int l,r;
	bool friend operator < (node c,node d){
		return c.r<d.r;
	}
}a[N]; vector<node>v[N];
int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;++i)
		scanf("%d",&a[i].l);
	for(int i=1;i<=n;++i)
		scanf("%d",&a[i].r);
	for(int i=1;i<=400;i++){
		for(int j=1;j<=n;j++){
			if(a[j].l<=i&&a[j].r>=i){
				v[i].push_back(node{j,a[j].r});
			}
		}
	}
	for(int i=1;i<=400;++i){
		sort(v[i].begin(),v[i].end());
	}
	while(q--){
		int l,r,ans=0;
		scanf("%d%d",&l,&r);
		for(int i=1;i<=400;i++) vis[i]=0;
		for(int i=l;i<=r;++i){
			for(auto t:v[i]){
				if(!vis[t.l]){
					vis[t.l]=1;
					ans++;
					break;
				}
			}
		}
		printf("%d\n",ans);
	}
}

解法二:

其实这就是一个二分图,对于羊儿能到达区间的每一个点建一条边,然后在询问区间内跑一遍匈牙利就行了。

#include<bits/stdc++.h>
using namespace std;
vector<int>e[1000];
int vis[1000],match[1000],a[1000],b[1000];
int n,q;
int dfs(int x)
{
	for(int i=0;i<e[x].size();i++)
	{
		int y=e[x][i];
		if(!vis[y]){
			vis[y]=1;
			if(match[y]==0||dfs(match[y]))
			{
				match[y]=x;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		scanf("%d",&b[i]);
		for(int j=a[i];j<=b[i];j++){
			e[j].push_back(i);
		}
	}
	while(q--){
		int ans=0,l,r;
		scanf("%d%d",&l,&r);
		memset(match,0,sizeof match);
		for(int i=l;i<=r;i++)
		{
			memset(vis,0,sizeof(vis));
			if(dfs(i)) ans++;
		}
		printf("%d\n",ans);
	}
	
}

 

E-数列

比赛时想到了一定会是一个大的循环节,但是对于剩下的部分位置不知道咋分配。。。

定义 1 2 3 ... k 为一段。那么我们就是要在n个位置构造sum段这样的1~k。

能得到的答案就是n - sum。所以sum越小,答案就越大。

设这sum段中长度最小的段长度为mn,长度最大的段长度为mx,可以发现如果我们已知了sum,那么要使这sum段的总和最小,就要使得mn和mx尽可能的接近,因为如果mx-mn>=2,那么段mx的最后一个元素的值为mx,段mn最后一个元素的值为mn,那么我们使段mx的长度-1,mn的长度加1。那么总和的变化就是总和=总和-mx+mn+1。因为mx-mn>=2,所以mx>mn+1。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,mx=1e9;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        if(1ll*i*(1+n/i)*(n/i)/2+1ll*(n%i)*(n/i+1)<=m) mx = min(mx,i);
    for(int i=1;i<=n%mx;i++){
        for(int j=1;j<=n/mx+1;j++)
        printf("%d ",j);
    }
    for(int i=1;i<=mx-n%mx;i++){
        for(int j=1;j<=n/mx;j++)
        printf("%d ",j);
    }
    puts("");
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值