NYIST前缀和,差分,位运算训练题解



关于学长太善良出了一道朴素差分题。

题目大意:
给你一个n和m,分别表示区间的长度和操作的次数,然后m次询问每次有三个数l,r,c,表示区间 l 到r 的草长c单位的长度,问最后每个位置草长度为多少

这一题是差分的模板,不了解的话可以看这篇博客戳我查看

代码如下

#include<stdio.h>
int a[100005],b[100005];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
    int l,r,c;
	while(m--){
		scanf("%d%d%d",&l,&r,&c);
		b[l]+=c;
		b[r+1]-=c;
	}
    for(int i=1;i<=n;i++){
        b[i]+=b[i-1];
        printf("%d ",b[i]);
    }
	return 0;
}

派小星点星

给你一个n和m,分别表示区间的长度和操作的次数,之后输入n个整数,表示数组里的数,然后m次询问每次有三个数l,r,表示询问区间 l 到r 的总数是多少

同上题一样这是前缀和的模板,暴力做法会TLE,上面那篇博客也有前缀和的讲解

代码如下

#include<stdio.h>
int a[200005],b[200005];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        b[i]=b[i-1]+a[i];
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",b[r]-b[l-1]);
    }
    return 0;
}

重生之我在异世界学差分

和第一道题一模一样,但是这道题不用差分做会超时,这里不再罗嗦了

最大连续子段的异或和

题目大意:
给出一个正整数序列,求异或和最大的连续子段和。

这道题数组很小,只有100,所以我们可以把每个区间都算一遍,代码中 i 相当于是左端点,j相当于是右端点,可以知道两重for循环可以不重不漏的把所有区间都算完。

代码如下:

#include<stdio.h>
int t[1005]; 
int main(){
	int n,ans=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&t[i]);
	for(int i=1;i<=n;i++)
	{
	
		int sum=0;   //将sum设为0
		for(int j=i;j<=n;j++)
		{
			sum^=t[j];
			if(sum>ans) ans=sum;   //每异或一次,就和答案取最大值
		}
	}	
	printf("%d\n",ans);
	return 0;
}

肥波纳妾

题目大意
在这里插入图片描述
第一行是询问的次数t,接下来每行一个整数n表示上面这个序列的第n个数是多少

从序列中我们可以发现第n个数是第n-1个数和第n-2个数的和,所以把前两个数输入进去,接下来的数都可以推出来

#include<stdio.h>
int a[50];
int main(){
    int t,n,i;
    a[1]=1,a[2]=1;
    scanf("%d",&t);
    for(i=3;i<=40;i++){
        a[i]=a[i-1]+a[i-2];
    }
    while(t--){
        scanf("%d",&n);
        printf("%d\n",a[n]);
    }
    return 0;
}

杨辉三角(Easy)

题目大意:

如果我们按杨辉三角从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:
1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, …
给定一个正整数 n ,请你输出数列中第一次出现 n 是在第几个数?

在这里插入图片描述
如果把杨辉三角画成这样,我们可以发现一个数是它上方和上方左边的和,如果在边界的话,它就是1
由此我们可以处理出前100行然后一行一行的找答案

代码如下:

#include<stdio.h>
int a[105][105];
int main()
{
    int t;
    scanf("%d",&t);
     for(int i=1;i<=101;i++)
        {
            for(int k=1;k<=i;k++)
            {
                if(k==1||k==i) a[i][k]=1; //表示在边界
                else  a[i][k]=a[i-1][k-1]+a[i-1][k];
            }
        }
    while(t--)
    {
        int n;
        scanf("%d",&n);
        int num = 0;
        int flag = 0;
        for(int i=1;i<=101;i++)
        {
            for(int k=1;k<=i;k++)
            {
                num ++ ;
                if(a[i][k]==n)  //表示我们找到了答案
                {
            		flag = 1;
                    break;
                }
            }
            if(flag)break;
        }
      	printf("%d\n",num);
    }
    return 0;
}

取石子(pro plus max 版)

题目大意:
给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。

这道题涉及到了位运算,代码简单但思维难度较大,证明过程水平有限,大家用心看一下

首先结论: 如果初始所有值的异或结果不为0,则先手必胜,反之必败

我们可以知道0^ 0 ^… ^ 0 ^ 0=0
然后有两个推论

当序列异或的结果不为0的时候,我们必然有种方式拿走石子使异或结果为0
当序列异或的结果为0的时候,我们无论怎么拿走石子异或结果都不会再为0

证明如下

首先当a[1] ^ a[2] ^ … ^ a[n] = x != 0 时
记x的二进制表示中最高位的1是第k位,则肯定存在一个a[i]的第k位为1(因为要使两个数异或结 果 为1,必然要有一个1)
可知a[i]^x < a[i](因为x的最高位的1在k,第k位都为1,因此异或后第k为0,比k位低的异或结果不 管是怎么样的,最终结果肯定比a[i])
因此可以把a[i]变成a[i]^x
这样结果会变成a[1] ^ a[2] ^ … ^ a[i] ^ x ^ … ^ a[n] = 0

当a[1] ^ a[2] ^ … ^ a[n]= 0 (式1)时
当从任意一堆中拿走任何数量的石子后,异或结果肯定不等于0
反证法证明:
假设从任意一堆a[i]中拿走任何数量的石子后(a[i]变成a[t]),异或结果等于0,则a[1] ^ a[2] ^ … ^ a[t] ^ … a[n] = 0(式2)
则 式1^式2 = 0 -> a[t] ^ a[i] = 0 -> a[t] = a[i] , 这与a[i] > a[t]矛盾,因此假设不成立

所以当序列异或结果不为0时,先手可以让异或结果变成0,后手只能让异或结果不为0,然后如此往复,最后一定是先手到达 0^ 0 ^… ^ 0 ^ 0=0,的状态,也就是胜利,反之,先手必败
代码如下:

#include <stdio.h>

int main()
{
    int res=0,n=0;
    scanf("%d",&n);
    while(n--)
    {
        int x;
        scanf("%d",&x);
        res ^= x;
    }
    if(res) printf("Yes");
    else printf("No");
    return 0;
}

重生之我在异世界遇到差分。

题目大意:
给你长度为n的路,每个点都有一个怪物,血量是ai,你可以对某个区间的所有怪物造成1点伤害,但是如果这个点怪物血量为0,还对这个点造成伤害的话是不允许的,问你最少进行多少次操作可以击败所有怪物。

这道题我们可以这样想,
1.如果ai>ai-1,那么答案只用加上ai-ai-1就行了,因为我们完全可以在减ai-1的血量的时候顺便再把ai算上,最后只需要多算它们两个的差就行了。
2.如果ai<=ai-1,那么答案就不用更新,因为我们在减ai-1的时候就顺带着可以把ai的血量减完了

代码如下:

#include<stdio.h>
int a[2000005];
int main(){
    int n,sum=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(a[i]-a[i-1]>0) sum+=a[i]-a[i-1];
    }
    printf("%d",sum);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuewuguai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值