2019-08-18 纪中NOIP模拟A组

T1 [JZOJ6309] 完全背包

题目描述

数据范围

  

分析

  首先显然可以把体积大价值小,或者相同体积中价值较小的物品舍弃

  然后有这样一个定理,在 $n$ 个整数中一定存在若干个整数之和为 $n$ 的倍数

  证明就是在所有前缀和(包括第 $0$ 项)中,必定存在两个前缀和模 $n$ 的余数相等,所以这两个数之间的区间和(前开后闭)是 $n$ 的倍数

  设 $s$ 为性价比最高的物品中 $a_i$ 最小的物品,$x$ 为最优情况下非 $s$ 物品的种类

  当 $x \geq a_s$ 时,可以将若干个 $a_i$ 之和为 $a_s$ 倍数的物品用 $s$ 替换,此时结果一定不会更劣

  所以一定存在 $x < a_s$ 的最优方案,这 $x$ 个物品的 $a_i$ 之和一定不会超过 $100a_s$

  这样我们就可以先取 $\lfloor \frac{m}{a_s} \rfloor - 100$ 个 $s$ 物品,再在剩下的空间里做多重背包

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <set>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 1000005

int n, tot, last;
ll m, f[N];

struct Data {
    int a, b;
} w[N], v[N];

bool cmp(Data x, Data y) {
    if (x.a != y.a) return x.a < y.a;
    return x.b > y.b;
}

int main() {
    scanf("%d%lld", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &w[i].a, &w[i].b);
    sort(w + 1, w + n + 1, cmp);
    for (int i = 1; i <= n; i++)
        if (w[i].b > last)
            last = w[i].b, v[++tot] = w[i];
    Data best = v[1];
    for (int i = 2; i <= tot; i++)
        if (best.a * v[i].b > v[i].a * best.b) best = v[i];
    ll t = m / best.a - 100;
    ll ans = t * best.b;
    m -= t * best.a;
    for (int i = 1; i <= tot; i++)
        for (int j = v[i].a; j <= m; j++)
            f[j] = max(f[j], f[j - v[i].a] + v[i].b);
    printf("%lld\n", ans + f[m]);

    return 0;
}
View Code

T2 [JZOJ6208] 中间值

题目描述

数据范围

  

分析

  这题的原型是求两个有序数列中的第 $k$ 大/小数,通常这类题有两种 $O(log \; n)$ 的做法(以第 $k$ 小为例)

  一种是在两个序列中分别取第 $\frac{k}{2}$ 小的数,此时取出的数较小的序列的前 $\frac{k}{2}$ 个数中一定不存在答案,所以就可以缩小区间查找第 $k-\frac{k}{2}$ 小的数,直到有一个序列的长度变为 $0$

  另一种是在两个序列中分别取中位数 $a_i,b_j$,假设 $a_i \leq b_j$,若 $i+j \leq k$,则答案一定不在 $a$ 的前半段,否则答案一定不在 $b$ 的后半段,每次操作都将一个序列的长度减半

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <set>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 500005

int n, m, opt, ans;
int a[N], b[N];

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if ( ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x *= f;
}

int kth(int al, int ar, int bl, int br, int k) {
    if (al > ar) return b[bl + k - 1];
    if (bl > br) return a[al + k - 1];
    if (k == 1) return min(a[al], b[bl]);
    int mid = k >> 1, an = inf, bn = inf;
    if (al + mid - 1 <= ar) an = a[al + mid - 1];
    if (bl + mid - 1 <= br) bn = b[bl + mid - 1];
    if (an <= bn) return kth(al + mid, ar, bl, br, k - mid);
    else return kth(al, ar, bl + mid, br, k - mid);
}

int main() {
    n = read(); m = read();
    for (int i = 1; i <= n; i++) a[i] = read();
    for (int i = 1; i <= n; i++) b[i] = read();
    while (m--) {
        opt = read();
        if (opt == 1) {
            int x = read(), y = read(), z = read();
            if (!x) a[y] = z; else b[y] = z;
        }
        else {
            int l1 = read(), r1 = read(), l2 = read(), r2 = read();
            ans = kth(l1, r1, l2, r2, (r1 - l1 + r2 - l2 + 3) / 2);
            printf("%d\n", ans);
        }
    }

    return 0;
}
View Code

T3 [JZOJ6306] Sequence

题目描述

数据范围

  

分析

  题解说很显然,我也不是很清楚为什么,但 $f(x)$ 确实是个积性函数

  因此若 $n= \prod p_i^{c_i}$,则 $f(n)= \prod f(p_i^{c_i})$

  所以我们只需要处理出所有质数幂的函数值,就可以推出其他所有数的函数值

  对于质数 $p$,令 $d$ 为满足 $p_x \mid B$ 的最大的 $x$,根据容斥原理,有

$$f(p^c)= \begin{cases} \sum_{i=0}^c p^i [(c-i+1)^n-(c-i)^n] & {c \leq d} \\ \sum_{i=0}^{d-1} p^i [(c-i+1)^n-(c-i)^n] + p^d (c-d+1)^n & {c>d} \end{cases}$$

  由于数据范围较大,所以选择用欧拉筛,筛到某个质数时,求出该质数所有幂的函数值,以及其部分倍数的函数值

  同时在求 $f[i]$ 时,还可以求出 $i$ 除去它最小质因子的最高次幂得到的数,用 $s[i]$ 表示

  于是当 $p[j]$ 不是 $i$ 的最小质因数时,显然 $i$ 与 $p[j]$ 互质,此时 $f[i \times p[j]]=f[i] \times f[p[j]]$

  当 $p[j]$ 是 $i$ 的最小质因数时,则有 $f[i \times p[j]]=f[s[i]] \times f[\frac{i \times p[j]}{s[i]}]$

#include <iostream>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 20000005

const int p = 998244353;
int m, prm[2000000], vis[N], s[N];
ll n, B, ans, pw[30], f[N];

ll qpow(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1) res = res * x % p;
        x = x * x % p; y >>= 1;
    }
    return res;
}

int main() {
    scanf("%lld%d%lld", &n, &m, &B);
    for (int i = 1; i <= 25; i++) pw[i] = qpow(i, n);
    ans = f[1] = 1;
    for (int i = 2; i <= m; i++) {
        if (!vis[i]) {
            prm[++prm[0]] = i; s[i] = 1;
            ll a = i, b = B; int c = 0, d = 0;
            while (a <= m) a *= i, c++;
            while (!(b % i)) b /= i, d++;
            for (int j = 1, x = i; j <= c; j++, x *= i) {
                if (j <= d)
                    for (int k = 0, y = 1; k <= c; k++, y *= i)
                        f[x] = (f[x] + y * (pw[j - k + 1] - pw[j - k] + p) % p) % p;
                else {
                    for (int k = 0, y = 1; k < d; k++, y *= i)
                        f[x] = (f[x] + y * (pw[j - k + 1] - pw[j - k] + p) % p) % p;
                    f[x] = (f[x] + qpow(i, d) * pw[j - d + 1] % p) % p;
                }
            }
        }
        for (int j = 1; j <= prm[0]; j++) {
            if (i * prm[j] > m) break;
            vis[i * prm[j]] = 1;
            if (!(i % prm[j])) {
                f[i * prm[j]] = f[s[i]] * f[i * prm[j] / s[i]] % p;
                s[i * prm[j]] = s[i];
                break;
            }
            f[i * prm[j]] = (f[i] * f[prm[j]]) % p;
            s[i * prm[j]] = i;
        }
        ans = (ans + f[i]) % p;
    }
    printf("%lld\n", ans);
    
    return 0;
}
View Code
posted @ 2019-08-18 20:50  Pedesis  阅读(205)  评论(0编辑  收藏  举报