前缀和算法复习牛客竞赛题库

链接:https://ac.nowcoder.com/acm/problem/14556
来源:牛客网

tabris有一个习惯,无聊的时候就会数圈圈,无论数字还是字母。
现在tabris更无聊啦,晚上睡不着觉就开始数羊,从a只数到b只。
顺便还数了a到b之间有多少个圈。

但是tabris笨啊,虽然数羊不会数错,但很可能数错圈的个数。
但是tabris很难接受自己笨这个事实,所以想问问你他一共应该数出多少个圈,这样tabris才好判断他到底笨不笨啊。

我们可以知道圈数是1的有0,4,9,6
圈数是2的有:8
因此我们可以先前缀和预处理一下每个数字有多少个圈
然后t次询问:每次输入a和b然后pre[b]-pre[a-1]就可以了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
ll pre[N];
int main()
{
    pre[0]=1;//初始化0数字圈的个数是1
    ll n;
    cin>>n;
    for(int i=1;i<=1e6;i++)
    {
        ll j=i;
        ll cnt=0;
        while(j)
        {
            ll k=j%10;
            if(k==0||k==4||k==9||k==6)cnt+=1;
            else if(k==8)cnt+=2;
            j/=10;
        }
        pre[i]=pre[i-1]+cnt;
    }
    while(n--)
    {
    ll a,b;
    cin>>a>>b;
    cout<<pre[b]-pre[a-1]<<endl;
    }
}

杭州人称傻乎乎的人为62,而嘟嘟家这里没有这样的习俗。

相比62,他那里的人更加讨厌数字38,当然啦,还有4这个

数字!所以啊,嘟嘟不点都不想见到包含38或者4的数字。

每次给出一个区间[n,m],你能找到所有令人讨厌的数字吗?

也是比较板子的题目我们可以定义一个check函数如果某个数含有4或者38那么就标记一下这样前缀和的时候就可以加1

bool check(int x)
{
    while(x)
    {
        if(x%10==4||x%100==38) return  true;
        x/=10;
    }
    return false;
}

然后我们前缀和的预处理的时候就可pre[i]=pre[i-1]+check(i);

具体代码:

#include<iostream>
using namespace std;
const int N=1e6+10;
typedef long long ll;
ll pre[N];
bool check(int x)
{
    while(x)
    {
        if(x%10==4||x%100==38) return  true;
        x/=10;
    }
    return false;
}
int main()
{
    for(int i=1;i<=N-1;i++)
    {
        pre[i]=pre[i-1]+check(i);
    }
    int n,m;
    while(cin>>n>>m)
    {
        if(n==m&&n==0) break;
        cout<<pre[m]-pre[n-1]<<endl;
    }
    
    return 0;
}

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

今天qwb要参加一个数学考试,这套试卷一共有n道题,每道题qwb能获得的分数为ai,qwb并不打算把这些题全做完,
他想选总共2k道题来做,并且期望他能获得的分数尽可能的大,他准备选2个不连续的长度为k的区间,
即[L,L+1,L+2,....,L+k-1],[R,R+1,R+2,...,R+k-1](R >= L+k)。

这题怎么说呢

可以理解成找出最大k区间长度和以及次大k区间长度和的sum

8 2
-1 0 2 -1 -1 2 3 -1

可以看一下样例的

答案就是这两个区间的和:2+5=7

那应该怎么做这题呢?

首先我们看到区间长度肯定是要用前缀和的那我们先预处理一下

然后我们其实可以暴力去搜一下每个k区间的总和\sumi-\sumi-k

可以类比成滑动窗口一样就是区间长度为k的区间去移动每次更新最值

就是我们先定义一个maxn1先找到两个最值其中一个

maxn1=max(maxn1,pre[i]-pre[i-k])

那这里可能会有人问了为啥不是i-k-1

很简单的我们可以举一个例子

比如样例里面的0 2

实际上是pre[3]-pre[3-k];

所以在写前缀和的时候要注意区间范围

当我们找到一个最值那我们要找下一个最值也是同理

 maxn2=max(maxn2,maxn1+(pre[i+k]-pre[i]));

因为如果没有找到另外一个最值的话maxn1是不可能会被更新的会一直保留着

以下是具体代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll a[N];
ll pre[N];
ll sum;
void cf()
{
    memset(pre,0,sizeof pre);
    ll n,k;
    sum=0;
    cin>>n>>k;
    ll maxn1=-1e18;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
       pre[i]=pre[i-1]+a[i];
    }
    ll maxn2=-1e18;
    for(int i=k;i+k<=n;i++)
    {
        maxn1=max(maxn1,(pre[i]-pre[i-k]));
        //维护其中一个最值
        maxn2=max(maxn2,maxn1+(pre[i+k]-pre[i]));
    }
    cout<<maxn2<<endl;
    
    
    
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    ll t;
    cin>>t;
    while(t--)
    {
        cf();
    }
}

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

White Cloud is exercising in the playground.
White Cloud can walk 1 meters or run k meters per second.
Since White Cloud is tired,it can't run for two or more continuous seconds.
White Cloud will move L to R meters. It wants to know how many different ways there are to achieve its goal.
Two ways are different if and only if they move different meters or spend different seconds or in one second, one of them walks and the other runs.

题目大意:

白云在锻炼他每一次可以走一步或者走k步,但是不能连续两次走k步,(第一次走完k步第二次必须走一步),问从l-r又多少种走法

分析:

经典的dp+前缀和

​

//定义dp[i][0/1]
//dp[i][1]是最后一步走k步
//dp[i][0]是最后一步走1步
//表示走到距离是i的位置最后一步是走一步还是k步
//我们发现如果最后一步是走k步的话那前一步必须走1步才可以
//那么它的方案数和dp[i-1][0]一样
dp[i][1]=dp[i-k][0]
//如果最后一步是走1步到达距离是i的位置那前一步有两种可能:1.走1步 2.走k步
//那第i步的方案数就i-1步走1步和i-1步走k步的和
dp[i][0]=dp[i-1][0]+dp[i-1][1];

[点击并拖拽以移动]
​

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
const int mod=1e9+7;
ll pre[N];
ll dp[N][2];
void cf()
{
    ll q,k;
    cin>>q>>k;
    ll l,r;
    //q次询问,走k步
    dp[0][0]=1;//注意初始化第一次距离是0走0步也是一种
    for(int i=1;i<=100000;i++)
    {
        dp[i][0]=(dp[i-1][1]+dp[i-1][0])%mod;
        if(i>=k)
        {
            dp[i][1]=dp[i-k][0]%mod;
        }
        pre[i]=(pre[i-1]+dp[i][0]+dp[i][1])%mod;
    }
    while(q--)
    {
        cin>>l>>r;
        cout<<(pre[r]-pre[l-1]+mod)%mod<<endl;
        //这里加mod是因为取模完pre[r]可能会比pre[l-1]小
        //所以要加mod偏移量
    }
    
    
    
}
int main()
{
    
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    ll t;
    t=1;
    while(t--)
    {
        cf();
    }
}

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

 

考点:前缀和+按位贪心

我们可以按位贪心

xor:0^0=0,1^1=0,0^1=1,1^0=1;

如果某个位置上面0的个数比1多的话那我们的x这一位一定要取1这样

x&a[1]+x&a[2]+..才会最大

就比如

000

000

000

111

111

我们看第一位0的个数是3,1的个数是2

这个时候如果我们第1位是取0那这个位置上的答案就是

0^0+0^0+0^0+0^1+0^1=2个1

如果是取1的话就是:

0^1+0^1+0^1+1^1+1^1=3个1

所以某一个位置上0多的话x这个位置上要是1否则取0

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int pre[35][N];
//前缀和某个位置上1的个数
typedef long long ll;
int main()
{
	ll n;
    cin>>n;
	for(int i=1;i<=n;i++)
	{
		ll x;
        cin>>x;
		for(int j=0;j<31;j++)
        {
            pre[j][i]=pre[j][i-1]+((x>>j)&1);
//x>>j&1是判断这个数第j位上是0还是1
//pre[j][i]表示到i个数字的第j位上1的个数
        }
	}
	ll q,x;
    cin>>q;
	while(q--)
	{
        ll l,r;
		cin>>l>>r;
        x=0;
		for(int i=0;i<31;i++)
        {
            if(2*pre[i][r]-2*pre[i][l-1]<r-l+1)x|=(1<<i);
//如果1的个数*2小于区间里面数字的个数说明1小于0的个数
//x|(1<<i)是把x这一位赋值
//可以理解成x+=pow(2,i)
            }
		cout<<x<<endl;
	}
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值