The 2021 ICPC Asia Regionals Online Contest (II)

H.Set

随机。

#include <bits/stdc++.h>
using namespace std;
int main(int argc, char *argv[]) {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int k, r;
    cin >> k >> r;
    vector<int> T;
    int cnt1 = (512 - 1) / r + 1;
    for (int i = 1; i <= 256; ++i) {
        T.push_back(i <= cnt1 ? 1 : 0);
    }
    for (int i = 1; i <= k; ++i) {
        for (auto it: T) {
            cout << it;
        }
        cout << '\n';
        random_shuffle(T.begin(), T.end());
    }
    system("pause");
    return 0;
}

K.Meal

考虑状压\(dp\)\(dp[i]\)表示选菜状态为\(i\)的概率,例如\(1011\)表示选了第\(1,3,4\)道菜的状态,那么我们可以发现,因为题目保证学生是按顺序点菜的,那如果有\(i\)道菜选了,说明已经有\(i\)个学生点了菜,并且最后一个点菜的是第\(i\)个编号的学生,那么就可以进行状态转移了,我们在状态转移时顺便记录答案即可。

假如选了第\(1,3,4\)道菜,那么它可以由\(1.\)选了第\(1,3\)道菜,\(2.\)选了\(1,4\)道菜,\(3.\)选了第\(3,4\)道菜转移而来,即当前状态少一道菜的情况。

因为题目给定值域很小而枚举范围很大,所以我们可以考虑预处理逆元而不是每次单独计算。

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int MAXN = 2e3 + 5;
const int MOD = 998244353;

int inv[MAXN];
void getInvs() {
    inv[1] = 1;
    for (int i = 2; i < MAXN; ++i) {
        inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    }
}
int a[25][25], sum[25], dp[1 << 20], ans[25][25];
/*
dp[i]表示当前选菜状态的概率
*/
int32_t main(int argc, char *argv[]) {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    getInvs();
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> a[i][j];
            sum[i] += a[i][j];
        }
    }
    dp[0] = 1;
    // 枚举菜的状态
    for (int state = 0; state < (1 << n); ++state) {
        int idx = __builtin_popcount(state); // 统计state二进制下1的个数
        // 有idx道菜被选了就说明当前状态最后选菜的是第idx位学生
        int cnt = 0; 
        for (int i = 0; i < n; ++i) {
            if (state >> i & 1) {
                cnt += a[idx][i];
            }
        }
        for (int i = 0; i < n; ++i) {
            // 可以从没选这道菜转移过来
            if (state >> i & 1) {
                // 题目所给式子乘上上一个状态概率
                // 分母即为第idx个学生满意度之和减去选了的菜的和cnt, 因为是上一个状态, 没选i, 所以还要加上这个学生对第i道菜的满意度
                int prob = (dp[state ^ (1LL << i)] * a[idx][i] % MOD * inv[sum[idx] % MOD - cnt + a[idx][i]]) % MOD;
                ans[idx][i] = (ans[idx][i] + prob) % MOD;
                dp[state] = (dp[state] + prob) % MOD;
            }
        }
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cout << ans[i][j - 1] << " \n"[j == n];
        }
    }
    system("pause");
    return 0;
}

L.Euler Function

根据线性递推求欧拉函数我们可以得到以下结论:

对于要乘上的\(w\)我们对它分解质因数,先考虑单点\(x\)的情况,我们遍历\(w\)的所有质因子\((例如4的所有质因子包括[2,2])\)​,假如当前遍历到的为\(i\)

\(\bullet\) \(x\%i=0\)\(\phi(x \cdot i) = \phi(x) \cdot i\)

\(\bullet\) \(x\%i!=0\)\(\phi(x \cdot i) = \phi(x) \cdot (i-1)\)

对于区间乘我们可以分两种情况考虑,假如当前遍历的因子是\(i\)

\(\bullet\) 区间内每个数都有这个因子,那么我们只需要执行区间乘操作。

\(\bullet\) 否则我们直接单点暴力更新,单点乘上\(i-1\),同时给这个点的标记数组加上\(i\)这个因子。

那么本题就可以用线段树解决了,对于标记数组,可以考虑用\(bitset\)实现,这样上推的时候只需要左右儿子的\(bitset\&\)一下就可以了。

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int MAXN = 1e5 + 5;
const int MOD = 998244353;

bool isNotPrime[MAXN]; // 是否是质数
int phi[MAXN]; // 欧拉函数
int prime[MAXN], idx; // 质数
void seive(int _MAXN) {
    phi[1] = 1;
    for (int i = 2; i < _MAXN; ++i) {
        if (!isNotPrime[i]) {
            phi[i] = i - 1;
            prime[++idx] = i;
        }
        for (int j = 1; j <= idx && i * prime[j] < _MAXN; ++j) {
            isNotPrime[i * prime[j]] = true;
            // 情况一
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            // 情况二
            else {
                phi[i * prime[j]] = phi[i] * (prime[j] - 1);
            }
        }
    }
}
void divide(bitset<100>& bit, int x) {
    for (int i = 2; i * i <= x; ++i) {
        if (x % i == 0) {
            bit[i] = 1;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        bit[x] = 1;
    }
}

int a[MAXN];

#define ls(x) (x) << 1
#define rs(x) (x) << 1 | 1
struct Node {
    int l, r, val, mul;
    bitset<100> bit;
    int mid() {
        return (l + r) >> 1;
    }
    int len() {
        return r - l + 1;
    }
    void update(int x) {
        if (x) {
            val = (val * x) % MOD;
            mul = (mul * x) % MOD;
        }
    }
} tree[MAXN << 2];
void pushup(int p) {
    tree[p].bit = tree[ls(p)].bit & tree[rs(p)].bit;
    tree[p].val = (tree[ls(p)].val + tree[rs(p)].val) % MOD;
}
void pushdown(int p) {
    tree[ls(p)].update(tree[p].mul);
    tree[rs(p)].update(tree[p].mul);
    tree[p].mul = 1;
}
void build(int l, int r, int p) {
    tree[p].l = l, tree[p].r = r, tree[p].mul = 1;
    if (l == r) {
        tree[p].val = phi[a[l]];
        // 先将当前给定数组的因子全部保存下来
        divide(tree[p].bit, a[l]);
        return ;
    }
    int mid = (l + r) >> 1;
    build(l, mid, ls(p));
    build(mid + 1, r, rs(p));
    pushup(p);
}
void modify(int l, int r, int k, int p) {
    if (l <= tree[p].l && tree[p].r <= r) {
        if (tree[p].bit[k]) {
            tree[p].update(k);
            return ;
        }
        if (tree[p].l == tree[p].r) {
            tree[p].bit[k] = 1;
            tree[p].update(k - 1);
            return ;
        }
    }
    pushdown(p);
    int mid = tree[p].mid();
    if (l <= mid) modify(l, r, k, ls(p));
    if (r > mid) modify(l, r, k, rs(p));
    pushup(p);
}
int query(int l, int r, int p) {
    if (l <= tree[p].l && tree[p].r <= r) {
        return tree[p].val;
    }
    pushdown(p);
    int mid = tree[p].mid(), res = 0;
    if (l <= mid) res = (res + query(l, r, ls(p))) % MOD;
    if (r > mid) res = (res + query(l, r, rs(p))) % MOD;
    return res;
}
int32_t main(int argc, char *argv[]) {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    seive(105);
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    build(1, n, 1);
    while (m--) {
        int opt, l, r, w;
        cin >> opt >> l >> r;
        if (opt == 1) {
            cout << query(l, r, 1) << '\n';
        }
        else {
            cin >> w;
            for (int i = 2; i * i <= w; ++i) {
                int cnt = 0;
                while (w % i == 0) {
                    ++cnt;
                    w /= i;
                }
                while (cnt--) {
                    modify(l, r, i, 1);
                }
            }
            if (w > 1) {
                modify(l, r, w, 1);
            }
        }
    }
    system("pause");
    return 0;
}
posted @ 2021-09-27 19:25  stler  阅读(165)  评论(0)    收藏  举报