ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2021.11.3模拟赛 赛后总结

2021-11-03 21:34:15  阅读:170  来源: 互联网

标签:10 cnt le 2021.11 val int return 赛后 模拟


A. 质数统计(prime)

Description

给定 \(n\) 个正整数 \(a_1,a_2\dots a_n\),定义 \(Cnt(p)\) 表示这 \(n\) 个数中能被质数 \(p\) 整除的数的个数,不是质数的 \(Cnt()\) 为 \(0\)。

有 \(m\) 组询问,每组询问输入 \(l,r\),求 \(\sum_{i=l}^rCnt(i)\)

\(1\le n,m \le 10^6,1\le a_i \le 10^7,1\le l \le r \le 10^7\)

Solution

显然先预处理出 \(cnt\) 数组,做个前缀和,\(O(1)\) 查询。

可以用线性筛,同时求出每个数的最小质因子 \(mindiv\),对于每个 \(a_i\),不断除以 \(mindiv\),就可以得到每个质因子了,也就求出了 \(cnt\) 数组。

也可以不处理 \(mindiv\),就仿照质因数分解的过程也可以过

题上说要对 \(10^9+7\) 取模,但事实证明并不用。

Code
#include <bits/stdc++.h>
#define ll long long

using namespace std;

template <typename T>
void read(T &x)
{
    x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
    return;
}

template <typename T>
void write(T x)
{
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}

const int N = 1e6 + 5;
const int MAXN = 1e7 + 5;
const int P = 1e9 + 7;
int n, m, a[N], maxn;
bool flag[MAXN];
int p[N], tot, mindiv[MAXN];

void Euler(int n)
{
    flag[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!flag[i]) p[++tot] = i, mindiv[i] = i;
        for(int j = 1; j <= tot && i * p[j] <= n; j++)
        {
            flag[i * p[j]] = 1;
            mindiv[i * p[j]] = p[j];
            if(i % p[j] == 0) break;
        }
    }
    return;
}

ll cnt[MAXN];

void Divide(int x)
{
    while(x > 1)
    {
        cnt[mindiv[x]]++;
        int d = mindiv[x];
        while(x % d == 0) x /= d;
    }
    return;
}

int main()
{
    read(n), read(m);
    for(int i = 1; i <= n; i++)
        read(a[i]), maxn = max(maxn, a[i]);
    Euler(maxn);
    for(int i = 1; i <= n; i++)
        Divide(a[i]);
    for(int i = 1; i < MAXN; i++)
        cnt[i] += cnt[i - 1];
    while(m--)
    {
        int l, r;
        read(l), read(r);
        write(cnt[r] - cnt[l - 1]);
        putchar('\n');
    }
    return 0;
}

B. 城市规划(planning)

Description

给定 \(n\) 栋楼的高度 \(h_i\),定义这些楼的不美观度为 \(max_{1\le i\le n}\{|h_i-h_{i+1}|\}\)。

现在你可改变任意 \(k\) 栋楼的高度,使得不美观度最小。

输出最小值为多少。

\(1\le k \le n \le 2000,1\le h_i \le 2\times 10^9\)

Solution

观察到是最大值最小,不难想到二分答案。

对于二分的答案 \(x\),考虑如何判断是否可行

设 \(f_i\) 表示将前 \(i\) 栋楼改成合法的最少需要改几栋楼的高度

初值为 \(i-1\),转移时,枚举 \(j\),若 \(|h_i-h_j| \le x\times (i-j)\),就说明可以通过修改 \((i,j)\) 中间的 \(i-j-1\) 楼使得前 \(i\) 栋楼合法

\(f_i=min(f_i,f_j+i-j-1)\)

最后将 \(n\) 栋楼枚举一遍,判断是否存在 \(f_i+n-i \le k\)。

注意楼比较高,要开 long long

Code
#include <bits/stdc++.h>
#define int long long

using namespace std;

template <typename T>
void read(T &x)
{
    x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
    return;
}

template <typename T>
void write(T x)
{
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}

const int N = 2010;
int n, k, a[N], ans;
int f[N];

bool chk(int x)
{
    for(int i = 1; i <= n; i++)
    {
        f[i] = i - 1;
        for(int j = 1; j < i; j++)
            if(abs(a[i] - a[j]) <= 1ll * x * (i - j))
                f[i] = min(f[i], f[j] + i - j - 1);
    }
    for(int i = 1; i <= n; i++)
        if(f[i] + n - i <= k) return true;
    return false;
}

signed main()
{
    read(n), read(k);
    int l = 0, r = 0;
    for(int i = 1; i <= n; i++)
        read(a[i]), r = max(r, abs(a[i] - a[i - 1]));
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(chk(mid)) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    write(ans), putchar('\n');
    return 0;
}

C. 花环(garland)

Description

给定 \(n\) 个点的环,划分为 \(m\) 段,最大化和。

\(1\le n \le 3 \times 10^5,1\le m \le 10^5\)

Solution

首先注意到一个性质:如果有连续一段权值都为正的点,那么如果选择了一个,肯定选完一整段(可能分成几段来选)。

如果这样的段的个数 \(\le m\),那么答案即为这些段的和。

否则,要减少一些段,有两种方法可以减少一段:

  1. 少选一整段全为正的
  2. 选择一段全为负的,将两段正的连起来

所以一整段负的也是要么不选,要么全选

那么我们可以将这样连续的正的和负的缩成一个点,就变成了一个正负相间的环

考虑如何减到剩 \(m\) 段。

先把负的权值改为正的,然后每次选出一段就把和减掉,答案的初值为所有正段的和。

不难发现,如果选出第 \(i\) 段,那么两边的段一定不会再被选,因为如果第 \(i\) 段原来是正的,那么已经减少了一段,再选两边的会使答案更劣,如果第 \(i\) 段原来是负的,那么选两边也会使答案更劣。

但是我们不能只贪心的去选当前最优的,因为有可能再选了其他段后,另一种方案更优,所以要进行反悔贪心。

具体请见 Luogu P3620 数据备份

Code
#include <bits/stdc++.h>

using namespace std;

template <typename T>
void read(T &x)
{
    x = 0; int f = 1; char c = getchar();
    while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
    while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
    x *= f;
    return;
}

template <typename T>
void write(T x)
{
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}

const int N = 6e5 + 5;
typedef pair<int, int> P;

int n, m, a[N], ans;
int val[N], cnt;
int pre[N], nxt[N];
bool vis[N];
priority_queue <P, vector<P>, greater<P> > que;

void del(int x)
{
    vis[x] = 1;
    nxt[pre[x]] = nxt[x];
    pre[nxt[x]] = pre[x];
    return;
}

int main()
{
    read(n), read(m);
    for(int i = 1, lst = 0; i <= n; lst = a[i], i++)
    {
        read(a[i]);
        if(a[i] * lst > 0) val[cnt] += a[i];
        else val[++cnt] = a[i];
    }
    if(val[1] * val[cnt] > 0 && cnt > 1) val[1] += val[cnt], cnt--;

    n = cnt, cnt = 0;
    for(int i = 1; i <= n; i++)
        if(val[i] > 0) ans += val[i], cnt++;

    if(cnt <= m)
    {
        write(ans);
        return 0;
    }

    for(int i = 1; i <= n; i++)
    {
        pre[i] = i - 1, nxt[i] = i + 1;
        if(val[i] < 0) val[i] = -val[i];
        que.push(P(val[i], i));
    }
    pre[1] = n, nxt[n] = 1;

    while(cnt > m)
    {
        cnt--;
        while(vis[que.top().second]) que.pop();
        P p = que.top();
        que.pop();
        ans -= p.first;
        int id = p.second;
        val[id] = val[pre[id]] + val[nxt[id]] - val[id];
        del(pre[id]), del(nxt[id]);
        que.push(P(val[id], id));
    }
    write(ans);
    return 0;
}

D. 密码门(cipher)

Description

给定 \(n\) 个操作符和参数,操作符包括按位与、按位或、按位异或,求一个数通过这些操作后的值,多组询问,带修。

修改某个操作的操作符和参数。

\(1\le n,m \le 2\times 10^5,1\le x \le 1000\)

Solution

用线段树维护二进制每一位上一开始是 \(0\) 或 \(1\),最后结果是多少。

因为数最大只有 \(1000\),二进制也就 \(10\) 位,因此对每一位建个线段树还是可以的,不过貌似有压在一起的做法?不太会

pushup 就是将在左子树里求出来的结果放在右子树里再求

Code
#include <bits/stdc++.h>
#define ls (rt << 1)
#define rs (rt << 1 | 1)

using namespace std;

const int N = 2e5 + 5;
int n, m, a[N];
int op[N], x[N];
int t[10][N << 2][2];   // & | ^

int get_op(char c)
{
    if(c == 'A') return 0;
    if(c == 'O') return 1;
    if(c == 'X') return 2;
}

void pushup(int b, int rt)
{
    t[b][rt][0] = t[b][rs][t[b][ls][0]];
    t[b][rt][1] = t[b][rs][t[b][ls][1]];
}

void build(int b, int l, int r, int rt)
{
    if(l == r)
    {
        if(op[l] == 0) t[b][rt][0] = 0, t[b][rt][1] = a[l];
        if(op[l] == 1) t[b][rt][0] = a[l], t[b][rt][1] = 1;
        if(op[l] == 2) t[b][rt][0] = a[l], t[b][rt][1] = a[l] ^ 1;
        return;
    }
    int mid = (l + r) >> 1;
    build(b, l, mid, ls);
    build(b, mid + 1, r, rs);
    pushup(b, rt);
}

void update(int b, int pos, int op, int val, int l, int r, int rt)
{
    if(l == r)
    {
        if(op == 0) t[b][rt][0] = 0, t[b][rt][1] = val;
        if(op == 1) t[b][rt][0] = val, t[b][rt][1] = 1;
        if(op == 2) t[b][rt][0] = val, t[b][rt][1] = val ^ 1;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) update(b, pos, op, val, l, mid, ls);
    else update(b, pos, op, val, mid + 1, r, rs);
    pushup(b, rt);
}

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
    {
        char s[5];
        scanf("%s%d", s, &x[i]);
        op[i] = get_op(s[0]);
    }

    for(int i = 0; i < 10; i++)
    {
        for(int j = 1; j <= n; j++)
            a[j] = (x[j] >> i) & 1;
        build(i, 1, n, 1);
    }

    while(m--)
    {
        int typ, x;
        scanf("%d%d", &typ, &x);
        if(typ == 1)
        {
            int ans = 0;
            for(int i = 0; i < 10; i++)
                ans += t[i][1][(x >> i) & 1] << i;
            printf("%d\n", ans);
        }
        else
        {
            char s[5];
            int y, opt;
            scanf("%s%d", s, &y);
            opt = get_op(s[0]);
            for(int i = 0; i < 10; i++)
                update(i, x, opt, (y >> i) & 1, 1, n, 1);
        }
    }
    return 0;
}

标签:10,cnt,le,2021.11,val,int,return,赛后,模拟
来源: https://www.cnblogs.com/acestar/p/15505813.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有