2021-06-15每日一题

3679. 素数矩阵

给定一个整数 n,请你构造一个满足下列条件的 n×n 矩阵:
矩阵中的所有数字都是不超过 105 的非负整数。
矩阵中的所有数字都不是素数。
每行的数字之和都是素数。
每列的数字之和都是素数。
如果答案不唯一,则输出任意合理结果均可。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
输出格式
每组数据输出占 n 行,每行包含 n 个空格隔开的整数,表示你构造的满足条件的矩阵。
数据范围
1≤T≤10,
2≤n≤100

思路:0和1都不是素数,
每行每列 1+1=2,就能够保证每行每列都是素数了
如: 分别将主对角线上的数赋值为1,将主对角线前面一个数也赋值为1即可
1 0 0 1
1 1 0 0
0 1 1 0
0 0 1 1

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int a[300];
    int n,t;
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=0;i<n;i++)
        {for(int j=0;j<n;j++)
    //将主对角线前面一个数也赋值为1,保证不越界+n再取模即可
           if(i==j||j==(i-1+n)%n)
           cout<<1<<" ";
           else cout<<0<<" ";
           cout<<endl;}
           
    }
}

K倍区间

给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。
你能求出数列中总共有多少个 K 倍区间吗?
输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行包含一个整数 Ai。
输出格式
输出一个整数,代表 K 倍区间的数目。
数据范围
1≤N,K≤100000,
1≤Ai≤100000
输入样例:
5 2
1
2
3
4
5
输出样例:
6

思路: 改变暴力枚举每个区间是否能够整除k的思想。
- 利用一个区间(i~j)的和如能够整除k 那么说明(i~j)的数一定是k的倍数
- 即为(sum[ j ] - sum[ i ])% k == 0 判断,即sum[ j ] %k == sum[ i ]%k。
-所以我们可以用一个前缀和数组sum来累加区间的和,并且只要判断sum[ j ] 前出现了多少个模与它相等的前缀和,就知道以 j 结尾的子区间有几个是k区间了。
-我们用cnt[i]数组来存储模k余数为i的个数,并且要特殊处理一下cnt[0] :表示%k余数为0要初始化为1 因为 当我们处理到第一个sum[j]%k==0的时候 就表示从 0~j是一个k倍区间了 表示从0到j的区间和为k的倍数。

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll s[N],cnt[N];

int main(){
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++) {
        cin>>s[i];
        s[i]+=s[i-1];
    }
    long long  res=0;
    cnt[0]=1;//对余数为0赋值为1 表示 0 ~j为第一个k倍区间。
    for(int i=1;i<=n;i++){
          res+=cnt[s[i]%k];  //看前i个数有没有整除k并且余数为m的情况 有的话就表示 j~i他们模数相同并且s不相等说明中间有k的整数倍
         cnt[s[i]%k]++;  //将s[i]%k的余数的个数加1 加上本次循环的   
    }
    cout<<res;
}
  1. 扩充序列

给定一个只包含一个数字 1 的序列 [1]。现在,要对该序列进行 n−1 次扩充。
每次扩充时,首先要将序列本身添加到其自身的尾部,然后还要在两个序列中间插入还未使用过的最小正整数。
例如,序列 [1] 经过一次扩充后,得到序列 [1,2,1],再经过一次扩充后,得到序列 [1,2,1,3,1,2,1]。
现在,请你计算在经过 n−1 次扩充后,序列的第 k 个元素的值是多少?(元素从 1 开始编号)
输入格式
共一行,包含两个整数 n 和 k。
输出格式
输出一个整数,表示经过 n−1 次扩充后,序列的第 k 个元素的值。
数据范围
对于前三个测试点,1≤n,k≤10。
对于全部测试点,1≤n≤50,1≤k≤2n−1。

在这里插入图片描述

在这里插入图片描述

扩充 n 次,就会将原序列变为 2n−1 个数组成的序列(,而下一次扩充,会在 2n 的位置放上一个 n+1。
递归问题,第n-1次扩充中间那个数就是最大值即为n,
所以第 n 次扩充我们可以将它表达为:

原序列 + n + 原序列

可以将第n次扩充的序列分为三部分,第一部分为第n-1次扩充的序列,第二部分为数n,第三部分也是第n-1次扩充的序列。我们判断k的值是在哪个部分,如果k的值刚好等于2的n-1次方(也就是在第n-1次扩充后加的那个最大值)的位置就直接返回n,如果k的值小于2的n-1次方就说明k是在n-1次扩充的范围内,就在(n-1,k)次递归找,如果k在第三段(k大于2的n-1)次方,而且第三段是和第一段对称的,也是在n-1扩充递归中查询k的位置会变成减去前面那两段的个数,返回(n-1,k-1ll<<n-1).

#include <iostream>
using namespace std;
typedef long long ll;
ll dfs(ll n,ll k)
{
     if(k==1ll<<n-1) return n;
     else if(k<1ll<<n-1)  return dfs(n-1,k);
     else return dfs(n-1,k-(1ll<<n-1));
}
int main()
{
    ll n,k;
    cin>>n>>k;
     cout<<dfs(n,k)<<endl;
}

3774. 亮灯时长

自习室内有一个智能灯。
在 0 时刻,管理员会将打开电闸,并将灯点亮。
在 M 时刻,管理员会直接拉下电闸,此时,如果灯处于点亮状态,则会因为断电而熄灭。在 0∼M 之间有 n 个不同时刻,不妨用 a1,a2,…,an 表示,其中 0<a1<a2<…<an<M。
在这 n 个时刻中的每个时刻,管理员都会拨动一次智能灯的开关,使灯的状态切换(亮变灭、灭变亮)。
现在,你可以最多额外指定一个时刻(也可以不指定),让管理员在此时刻也拨动开关一次。注意选定的时刻不能与 a1,a2,…,an 相等。
你的目的是让亮灯的总时长尽可能长。
输出这个最大亮灯总时长。

#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+10;
 int s1[N],s2[N],a[N];
int main()
{
    int T;
    cin>>T;
    int n,m;
    while(T--)
    {   
        cin>>n>>m;
          for(int i=1;i<=n;i++) cin>>a[i];
           a[++n]=m;    //将长度m也放入数组,一同操作
           s1[n]=s2[n]=0;  //s1表示开灯的后缀和,s2表示关灯的后缀和并初始化
           for(int i=n-1;i>=0;i--)
           {   s1[i]=s1[i+1],s2[i]=s2[i+1];
           
            //判断i在第几段,如果是偶数次就表示它后面那次减去这次的长度为本次开灯的长度
                if(i%2==0)s1[i]+=a[i+1]-a[i];
            //如果为奇数就是表示关灯的时间同样用后缀和来维护
                else s2[i]+=a[i+1]-a[i];
           }
          int res=s1[0];    //如果只有一次开灯操作时间就为整个长度
          for(int i=0;i<n;i++)  //这里来枚举每次的开关灯区间的长度
          {
              int t=a[i+1]-a[i];   //如果长度为1就表示两次之间没有缝隙无法插入一次开关灯
                if(t==1)continue;
                 //这里找最大值相当于也将不插入开关灯的操作了
                else res=max(res,s1[0]-s1[i]+t-1+s2[i+1]); 
        //枚举每次操作,插入了一次开关灯那么对后面开关灯的时间就要翻转,
        //将前面奇数次开灯操作加上本次操作t-1加上后面关灯的维护区间
          }
          cout<<res<<endl;
          
    }
}

1952. 金发姑娘和 N 头牛

#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
const int INF=2e9; //预定义一个整型最大值
// 将三个区间分成三断 负无穷 中间 正无穷  差分
int main(){
    int n,x,y,z;
    cin>>n>>x>>y>>z;
    int l,r;
    map<int,int>map;
    while(n--){
        cin>>l>>r;
        map[-INF]+=x;  //第一段 小于温度就加x  -INF到l
        map[l]+=y-x;  //第二段为l到r 正好在温度范围内就加上y,因为是前缀和就要减去加上的x温度
        map[r+1]+=z-y;   // 同理第三段为r到INF正无穷 加上z减去y
        map[INF]-=z;    //减去z
    }
    int res=0,sum=0;
    for(auto& [k,v]:map){   //前缀和累加并找出最大值
        sum+=v;
        res=max(res,sum);
    }
    cout<<res<<endl;
    
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值