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;
}

浙公网安备 33010602011771号