牛客寒假算法基础集训营1上

简介: 笔记

A 九小时九个人九扇门


题意:给定n个数,随便组合,求出数字根分别为0-9的组合个数。

数字根:1、4、9 == 1 + 4 + 9 = 14 == 1 + 4 = 5, 所以数字1、4和9的数字根为5。

思路:dp, 01背包问题
结论性质:一个数的数字根等于这个数对9取模的结果(特别地,取模得
0则数字根为9)
定义:令𝑑𝑝[i][j]表示考虑了前𝑖个数,选择了一些数字使得求和对9取模得𝑗的方案
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
const int mod = 998244353;
int n;
int f[N][10];
int main() {
    scanf("%d", &n);
    f[0][0] = 1;
    for (int i = 1; i <= n + 1; i++) {
        int x; scanf("%d", &x); x %= 9;
        for (int j = 0; j < 9; j++) {
            f[i][(j + x) % 9] = (f[i][(j + x) % 9] + f[i - 1][j]) % mod;
            f[i][j] = (f[i][j] + f[i - 1][j]) % mod;
        }
    }
    for (int i = 1; i < 9; i++) {
        printf("%d ", f[n][i]);
    }
    printf("%d\n", f[n][0] - 1);
    return 0;
}

B 炸鸡块君与FIFA22


线段树解法

#include<bits/stdc++.h>
using namespace std;
const int M=2e5+5;
int n,q;
char str[M];
struct node{
  int val[3];
}tree[M*4];
void Up(node &rt,node &ls,node &rs){
    /*
        rt根,ls左,rs右(假设两个点) 
        rt 的0对应的值应该是ls的0对应的值加上,rs的(ls已经有的0值+要模出的是0)互余(3)的值
    */
  rt.val[0]=ls.val[0]+rs.val[(0+ls.val[0])%3];
  rt.val[1]=ls.val[1]+rs.val[(1+ls.val[1])%3];
  rt.val[2]=ls.val[2]+rs.val[(2+ls.val[2])%3];
}
void build(int L,int R,int p){
  if(L==R){
    if(str[L]=='W')tree[p].val[0]=tree[p].val[1]=tree[p].val[2]=1;
    else if(str[L]=='D')tree[p].val[0]=tree[p].val[1]=tree[p].val[2]=0;
    else {
      tree[p].val[0]=0; // % 3 == 0, 不变
      tree[p].val[1]=tree[p].val[2]=-1;
    }
    return;
  }
  int mid=L+R>>1;
  build(L,mid,p<<1);
  build(mid+1,R,p<<1|1);
  Up(tree[p],tree[p<<1],tree[p<<1|1]);
}
node query(int l,int r,int a,int L,int R,int p){
  if(l == L && R == r) return tree[p];
  int mid=L+R>>1;
  if(r<=mid)return query(l,r,a,L,mid,p<<1);
  else if(mid<l)return query(l,r,a,mid+1,R,p<<1|1);
  else {
    node rt;
    node ls=query(l,mid,a,L,mid,p<<1),rs=query(mid+1,r,a,mid+1,R,p<<1|1);
    Up(rt,ls,rs);
    return rt;
  }
}
int main(){
  scanf("%d %d",&n,&q);
  scanf("%s",str+1);
  build(1,n,1);
  for(int i=1;i<=q;i++){
    int l,r,s;
    scanf("%d %d %d",&l,&r,&s); 
    if(l>r)printf("%d\n",s);
    else{
      node ans=query(l,r,s%3,1,n,1);
      printf("%d\n",s+ans.val[s%3]);
    }
  }
  return 0;
}

C Baby’s first attempt on CPU


pass


D 牛牛做数论


问题一:由于𝜙 𝑥 = 𝑥 ∗ ς(1 − 1/𝑝𝑖),之中𝑝𝑖取遍𝑥的所有种类的质因 子(注意是种类而不是个数,如素因子2出现两次在公式里也只乘上一次),则𝐻 𝑥 = ς(1 − 1/𝑝𝑖),显然乘的项数越多、每一项越小𝐻(𝑥)就越小,而按照2,3,5,7,11 … …的顺序取𝑝𝑖就可以达到这一目标。


问题二:对于素数𝑝有𝜙 (𝑝) = 𝑝 − 1,因此𝐻 (𝑝) = (p-1)/p,直觉可以感到

这是挺大一数(毕竟𝐻(𝑥)最大不超过1),所以可以猜出取最大的素数𝑝即可

#include <bits/stdc++.h>
using namespace std;
#define int long long
int prime[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
int is_primer(int x) {
    for (int i = 2; i <= x / i; i++) {
        if(x % i == 0) return 0;
    }
    return 1;
}
int t;
signed main() {
    cin >> t;
    while(t--) {
        int n; cin >> n;
        if(n == 1) {
            puts("-1");
            continue;
        }
        int res1 = 1ll, res2 = n;
        for (int i = 0; ; i++) {
            if(res1 * prime[i] > n) break;
            res1 *= prime[i];
        }
        while(is_primer(res2) == 0) res2--;
        cout << res1 << ' ' << res2 << endl;
    }
    return 0;
}

E 炸鸡块君的高中回忆


签到


F 中位数切分


给定一个长为n的数组a和一个整数m,你需要将其切成连续的若干段,使得每一段的中位数都大于等于m,求最多可以划分成多少段。

简单做法:直接找比m大的 - 比m小的
证明二(不严谨):先假设数组里只有≥ 𝑚的数字们(想象有一个只
有𝑐𝑛𝑡1个区间,且这些区间都是满足中位数
2的数组),他们都自己作为一个区间,则一共
≥ 𝑚的要求的;
含≥ 𝑚的数的长为𝑐𝑛𝑡 • 然后考虑插一个< 𝑚的数字进去,这个数字需要和两个≥ 𝑚的数组组
成一个区间才可以满足中位数要求,因此原来两个区间变成了一个区
间,区间数减一,然后删掉这个数和某个≥ 𝑚的数,数组长变为
𝑐𝑛𝑡2 − 1; • (你可能会说这𝑐𝑛𝑡2 − 1 里有一个数其实是在刚才新组成的区间里,
这没问题吗,但这种新组成的区间并不影响上述操作,所以可以看作
一个≥ 𝑚的数字)
• 以此类推,可以看出每个< 𝑚数字的作用就是说区间数减一
void solve() {
  int n, m; cin >> n >> m;
    int cnt1 = 0, cnt2 = 0;
    rep(i, 0, n) {
        int x; cin >> x;
        if(x < m) cnt2++;
        else cnt1++;
    }
    if(cnt1 - cnt2 <= 0) cout << -1 << endl;
    else cout << cnt1 - cnt2 << endl;
}

G ACM is all you need


不会

相关文章
|
4月前
|
算法 机器人
|
11月前
|
人工智能 算法
|
11月前
|
算法 数据库 C语言
|
11月前
|
机器学习/深度学习 人工智能 算法