A. Sticker Album

你想要得到\(n\)张贴纸,每包礼物中等概率出现 \([A,B]\)范围内数量的贴纸,求需要买多少包礼物才能至少获得\(n\)张贴纸的期望次数

\(1 \leq n \leq 10^6,0\leq A,B\leq 10^6\)

题解:期望DP

  • 我们考虑从后往前进行\(dp\)

  • 设计状态为\(dp[i]\)代表手上有\(i\)张贴纸时,至少获得\(n\)张贴纸的期望次数

  • 显然初始状态:\(dp[n]=dp[n + 1]...dp[∞] = 0\)

  • 容易得到状态转移方程:

\[ dp[i] = \sum_{j = i + a}^{j = i+b}(dp[j] + 1) \times \frac{1}{b - a + 1} , A \neq 0
\]
  • 但是该题存在\(A = 0\)的情况,即自己转移到自己,这是一个经典的问题,我们不妨通过移项来解决该问题
\[ dp[i] =(dp[i]+1)\times \frac{1}{b - a + 1} + \sum_{j = i + a + 1}^{j = i+b}(dp[j] + 1) \times \frac{1}{b - a + 1}, A = 0 \\
dp[i] = \frac{b - a + 1}{b - a} + \sum_{j = i + a + 1}^{j = i+b}(dp[j] \times \frac{1}{b - a}), A = 0
\]
const int N = 2e6 + 10, M = 4e5 + 10;

int n, a, b;
double dp[N];
// dp[i] 代表手上有 i 张牌时,拿牌到 n 张牌的期望次数
// dp[n-∞] = 0 void solve()
{
cin >> n >> a >> b;
double sum = 0;
if (a == 0)
{
for (int i = n - 1; i >= 0; --i)
{
sum += dp[i + a + 1];
dp[i] += 1.0 / (b - a) * sum + 1.0 * (b - a + 1) / (b - a);
sum -= dp[i + b];
}
}
else
{
for (int i = n - 1; i >= 0; --i)
{
sum += dp[i + a];
dp[i] += 1.0 / (b - a + 1) * sum + 1;
sum -= dp[i + b];
}
}
cout << fixed << setprecision(5) << dp[0] << endl;
}

E. Party Company

题解

  • 考虑离线,由于对于一条链来说年龄单调,考虑将询问的左端点\(L\)挂在距离查询点最远的且年龄不超过\(R\)的节点上
  • 因为单调,所以可以通过树上倍增来实现离线查询
  • 然后在\(dfs\)的过程中回答询问,最朴素的想法是每次将节点的询问下放
  • 实际上我们可以通过权值树状数组记录每个左端点的次数,对于某个节点\(u\)来说,它的答案为树状数组中所有比它小的左端点的个数
int n, q, w[N], fa[N][20], ans[N], c[N];
vector<int> g[N], vec[N]; int lowbit(int x) { return x & -x; }
void add(int x, int val)
{
while (x < N)
{
c[x] += val;
x += lowbit(x);
}
}
int get_sum(int x)
{
int res = 0;
while (x > 0)
{
res += c[x];
x -= lowbit(x);
}
return res;
} void dfs1(int u, int par)
{
if (u == 1)
fa[u][0] = 1;
else
fa[u][0] = par;
for (int i = 1; i <= 18; ++i)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
vector<int> tmp;
for (auto v : g[u])
{
if (v == par)
continue;
tmp.push_back(v);
dfs1(v, u);
}
} void dfs2(int u, int par)
{
for (auto l : vec[u])
add(l, 1);
ans[u] = get_sum(w[u]);
for (auto v : g[u])
{
if (v == par)
continue;
dfs2(v, u);
}
for (auto l : vec[u])
add(l, -1);
} void solve()
{
cin >> n >> q;
for (int i = 1; i <= n; ++i)
{
int u;
cin >> w[i] >> u;
if (i == 1)
continue;
g[u].push_back(i);
g[i].push_back(u);
}
dfs1(1, 0);
for (int i = 1; i <= q; ++i)
{
int u, l, r;
cin >> u >> l >> r;
for (int j = 18; j >= 0; --j)
{
if (w[fa[u][j]] <= r)
u = fa[u][j];
}
vec[u].push_back(l);
}
dfs2(1, 0);
for (int i = 1; i <= n; ++i)
cout << ans[i] << "\n "[i < n];
}

H. SBC's Hangar

给定\(n,k\),给定每个商品的重量\(w[i]\),要求从\(n\)个商品中挑选\(k\)个商品,使得重量范围在\([L,R]\)内的方案数

\(1 \leq n,k \leq 50\)

题解:数位\(DP\)

  • 考虑到\(n \leq 50\)
  • 考虑将所有商品排序后进行状态压缩,状态\(i\)代表\(n\)个商品选择的二进制状态
  • 然后求出第一个大于等于\(L\)的二进制\(l\),最大的小于等于\(R\)的二进制\(r\)
  • 然后现在将问题转化为:在二进制\([l,r]\)的范围内存在多少个二进制中的\(1\)的个数为\(k\)
  • 考虑差分后数位\(dp\)即可
int n, k, a[N], L, R, dp[N][N][2][2], num[N];

int dfs(int pos, int sum, bool lead, bool limit)
{
if (pos == 0)
return (sum == k ? 1 : 0);
if (dp[pos][sum][lead][limit] != -1)
return dp[pos][sum][lead][limit];
int res = 0;
int up = (limit ? num[pos] : 1);
for (int i = 0; i <= up; ++i)
{
if (i == 0 && lead)
res += dfs(pos - 1, sum, true, limit && i == up);
else if (i == 0)
res += dfs(pos - 1, sum, false, limit && i == up);
else
res += dfs(pos - 1, sum + 1, false, limit && i == up);
}
return dp[pos][sum][lead][limit] = res;
} int cal(int x)
{
int len = 0;
while (x)
{
num[++len] = x % 2;
x /= 2;
}
memset(dp, -1, sizeof dp);
return dfs(len, 0, true, true);
} int check(int mid)
{
int res = 0;
for (int i = 0; i < n; ++i)
{
if (mid >> i & 1)
res += a[i];
}
return res;
} void solve()
{
cin >> n >> k;
for (int i = 0; i < n; ++i)
cin >> a[i];
sort(a, a + n);
cin >> L >> R;
int l = 0, r = (1ll << n) - 1;
while (l <= r)
{
int mid = l + r >> 1;
if (check(mid) >= L)
r = mid - 1;
else
l = mid + 1;
}
int x = l;
l = 0, r = (1ll << n) - 1;
while (l <= r)
{
int mid = l + r >> 1;
if (check(mid) <= R)
l = mid + 1;
else
r = mid - 1;
}
int y = r;
cout << cal(y) - cal(x - 1) << endl;
}

I. Interactivity

题解:树形\(DP\)

  • 因为每颗子树中最多只能有一个叶子节点的值未知

  • 所以考虑设计状态\(dp[u][0/1]\)代表以\(u\)为根的子树中,\(0\)代表所有叶子节点的值都已知,\(1\)代表还有\(1\)个叶子节点的值未知

  • 考虑状态转移:

\[ dp[u][1] = \sum_{v_1 \in u的子树} dp[v_1][1] \times \prod_{v_2 \in u \and v_2\neq v_1} dp[v_2][0]\\
dp[u][0] = \prod dp[v][0] + dp[u][1]
\]

对于\(dp[u][0]\)为什么可以从\(dp[u][1]\)转移过来,原因是如果有\(1\)个叶子节点的值位置,我可以通过查询\(u\)节点的值使得所有叶子节点的值已知,所以允许有\(1\)个叶子节点未知

  • \(tips\):在\(dp[u][1]\)转移的过程中,会出现需要在一个序列中刨除一个数的问题,这里直接乘逆元会出错,对于这个经典的问题,我们考虑预处理前后缀即可
int n, dp[N][2];
vector<int> g[N];
// dp[u][0] : 以u为根的子树中叶已知所有叶子节点的值的方案数
// dp[u][1] : 以u为根的子树中仍存在1个叶子节点的值未知的方案数 void dfs(int u)
{
if ((int)g[u].size() == 0)
{
dp[u][0] = dp[u][1] = 1;
return;
}
for (auto v : g[u])
dfs(v);
vector<int> pre((int)g[u].size() + 10, 1), suf((int)g[u].size() + 10, 1);
for (int i = 0; i < (int)g[u].size(); ++i)
{
int v = g[u][i];
pre[i] = (i == 0 ? dp[v][0] : pre[i - 1] * dp[v][0] % mod);
}
for (int i = (int)g[u].size() - 1; i >= 0; --i)
{
int v = g[u][i];
suf[i] = (i == (int)g[u].size() - 1 ? dp[v][0] : suf[i + 1] * dp[v][0] % mod);
}
for (int i = 0; i < (int)g[u].size(); ++i)
{
int v = g[u][i];
if (i == 0)
dp[u][1] = (dp[u][1] + dp[v][1] * suf[i + 1] % mod) % mod;
else if (i == (int)g[u].size() - 1)
dp[u][1] = (dp[u][1] + dp[v][1] * pre[i - 1] % mod) % mod;
else
dp[u][1] = (dp[u][1] + dp[v][1] * pre[i - 1] % mod * suf[i + 1] % mod) % mod;
}
dp[u][0] = (dp[u][0] + dp[u][1]) % mod;
dp[u][0] = (dp[u][0] + suf[0]) % mod;
} void solve()
{
cin >> n;
for (int i = 2, u; i <= n; ++i)
{
cin >> u;
g[u].push_back(i);
}
dfs(1);
cout << dp[1][0] << endl;
}

K. Between Us

给定\(P\)个人和\(F\)对关系,每对关系代表\(u\)和\(v\)是朋友,现在要求将所有的人最多分成两组,使得每个人在同一组中的朋友数量为奇数,询问是否有解

题解:高斯消元解异或方程组

  • 假设分成的组的类别为\(0, 1\)

  • 令\(a[i]\)为\(i\)的组别,即\(a[i]=0/1\)

  • 如果\(u\)的朋友数量为奇数:

  • 如果\(u\)在\(0\)中,那么他会有奇数个朋友也在\(0\)中,偶数个朋友会在\(1\)中

  • 如果\(u\)在\(1\)中,那么他会有奇数个朋友也在\(1\)中,偶数个朋友会在\(0\)中

容易发现:对于\(u\)来说,其所有朋友分组的异或和等于\(a[u]\),即\(a[u] \oplus a[i] \oplus a[j]...a[k] = 0\)

  • 如果\(u\)的朋友数量为偶数:

  • 如果\(u\)在\(0\)中,那么他会有奇数个朋友也在\(0\)中,奇数个朋友会在\(1\)中

  • 如果\(u\)在\(1\)中,那么他会有奇数个朋友也在\(1\)中,奇数个朋友会在\(0\)中

容易发现:对于\(u\)来说,其所有朋友分组的异或和等于\(1\),即$ a[i] \oplus a[j]...a[k] = 1$

  • 列出\(P\)个异或方程组后高斯消元即可
int n, m, deg[N];
vector<int> g[N]; bitset<N> a[N];
int ans[N], Free[N], cnt; // 自由变量
int Gauss(int equ, int var)
{
int row, col, MaxRow;
col = 0;
for (row = 0; row < equ && col < var; row++, col++)
{
MaxRow = row;
for (int i = row + 1; i < equ; i++)
if (abs(a[i][col]) > abs(a[MaxRow][col]))
MaxRow = i;
if (MaxRow != row)
swap(a[row], a[MaxRow]);
if (a[row][col] == 0)
{
row--;
Free[++cnt] = col;
continue;
}
for (int i = row + 1; i < equ; i++)
{
if (a[i][col])
a[i] ^= a[row];
}
}
for (int i = row; i < equ; i++)
if (a[i][col])
return -1; // 无解
if (row < var)
return var - row; // 无穷解
for (int i = var - 1; i >= 0; i--)
{
ans[i] = a[i][var];
for (int j = i + 1; j < var; j++)
if (a[i][j])
ans[i] ^= (a[i][j] && ans[j]);
}
return 0; // 唯一解
} void solve()
{
cin >> n >> m;
for (int i = 1; i <= m; ++i)
{
int u, v;
cin >> u >> v;
--u, --v;
g[u].push_back(v);
g[v].push_back(u);
++deg[u], ++deg[v];
}
// n * (n + 1) 的增广矩阵
for (int i = 0; i < n; ++i)
{
if (!deg[i])
{
cout << "N" << endl;
return;
}
for (auto v : g[i])
a[i].set(v);
if (deg[i] & 1)
a[i].set(i);
else
a[i].set(n);
}
int res = Gauss(n, n);
if (res == -1)
cout << "N" << endl;
else
cout << "Y" << endl;
}

M. Machine Gun

题解:二维偏序

  • 考虑将所有敌人也通过两条射线投影到\(y\)轴上,那么会在\(y\)轴上形成\(n\)段区间\([l_i,r_i]\)

  • 那么对于每个询问,我们也将其投影在\(y\)轴上的区间\([L,R]\)求出,然后就是求与该区间相交的所有区间的敌人编号

  • 我们考虑什么情况是相交的:\(r_i \geq L \and l_i \leq R\)

  • 显然是一个二位偏序问题,考虑将条件转化到二维平面上,横坐标为\(l\),纵坐标为\(r\)

  • 那么每个敌人的区间就是平面上一点,我所要求的就是一个向上无限延伸的矩形中的点的编号

  • 因为我们要的是一个前缀中所有纵坐标大于等于\(L\)的点的编号,所以我们考虑树状数组,对每个位置\(l\)开一个\(vector\),以二元组形式存放该位置上所有点的纵坐标和编号

  • 那么对于一次查询来说,我只需要查询树状数组前\(logR\)个\(vector\),然后在每个\(vector\)里面二分即可

  • 因为保证所有被查询到的敌人不超过\(1e6\),所以二分之后可以暴力将所有符合要求的敌人取出来

int n, q;
vector<int> vec;
array<int, 3> seg[N];
vector<array<int, 2>> c[N]; int lowbit(int x) { return x & -x; }
void add(int x, array<int, 2> val)
{
while (x < N)
{
c[x].push_back(val);
x += lowbit(x);
}
}
vector<int> query(int x, int y)
{
vector<int> res;
while (x > 0)
{
int p = lower_bound(all(c[x]), array<int, 2>{y, 0}) - c[x].begin();
for (int i = p; i < (int)c[x].size(); ++i)
{
if (c[x][i][0] >= y)
res.push_back(c[x][i][1]);
}
x -= lowbit(x);
}
return res;
} int find(int x) { return lower_bound(all(vec), x) - vec.begin() + 1; } void solve()
{
cin >> n >> q;
for (int i = 1; i <= n; ++i)
{
int x, y;
cin >> x >> y;
int l = 2 * y - x, r = 2 * y + x;
seg[i] = {r, l, i};
vec.push_back(l);
}
sort(all(vec));
vec.erase(unique(all(vec)), vec.end());
sort(seg + 1, seg + n + 1);
for (int i = 1; i <= n; ++i)
{
auto [r, l, id] = seg[i];
l = find(l);
add(l, array<int, 2>{r, id});
}
int ans = 0;
while (q--)
{
int a, b;
cin >> a >> b;
int x = (-1 - ((ans + a) % mod)), y = (ans + b) % mod;
int l = 2 * y + x, r = 2 * y - x;
r = upper_bound(all(vec), r) - vec.begin();
vector<int> vt = query(r, l);
sort(all(vt));
ans = 0;
int pow = 1;
for (int i = 0; i < (int)vt.size(); ++i)
{
ans = (ans + vt[i] * pow % mod) % mod;
pow = pow * 5782344 % mod;
}
cout << ans << endl;
}
}

N. Number Multiplication

题解

  • 直接上科技:大数质因子分解
  • 对每个数质因子分解后排序就好了
mt19937_64 rnd(time(0));
ll mx_fac;
// vector<int> fac;
map<int, int> mp; ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; } ll qpow(ll a, ll b, ll p)
{ // 快速幂
ll res = 1;
while (b)
{
if (b & 1)
res = (__int128)res * a % p;
a = (__int128)a * a % p;
b >>= 1;
}
return res;
} bool Miller_Rabin(ll p)
{ // 判断素数
if (p < 2)
return 0;
if (p == 2)
return 1;
if (p == 3)
return 1;
ll d = p - 1, r = 0;
while (!(d & 1))
++r, d >>= 1; // 将d处理为奇数
for (ll k = 0; k < 10; ++k)
{
ll a = rnd() % (p - 2) + 2;
ll x = qpow(a, d, p);
if (x == 1 || x == p - 1)
continue;
for (int i = 0; i < r - 1; ++i)
{
x = (__int128)x * x % p;
if (x == p - 1)
break;
}
if (x != p - 1)
return 0;
}
return 1;
} ll Pollard_Rho(ll x)
{
ll s = 0, t = 0;
ll c = (ll)rnd() % (x - 1) + 1;
int step = 0, goal = 1;
ll val = 1;
for (goal = 1;; goal *= 2, s = t, val = 1)
{ // 倍增优化
for (step = 1; step <= goal; ++step)
{
t = ((__int128)t * t + c) % x;
val = (__int128)val * abs(t - s) % x;
if ((step % 127) == 0)
{
ll d = gcd(val, x);
if (d > 1)
return d;
}
}
ll d = gcd(val, x);
if (d > 1)
return d;
}
} void divide_fac(ll x)
{
if (x < 2)
return;
if (Miller_Rabin(x))
{ // 如果x为质数
mx_fac = max(mx_fac, x); // 更新答案
// fac.push_back(x);
mp[x]++;
return;
}
ll p = x;
while (p >= x)
p = Pollard_Rho(x);
// 不用求质因子幂次时使用
while ((x % p) == 0)
x /= p;
divide_fac(x), divide_fac(p); // 继续向下分解x和p
// divide_fac(x / p), divide_fac(p);
} int m, n, k; void solve()
{
cin >> m >> n >> k;
for (int i = 1; i <= n; ++i)
{
int x;
cin >> x;
divide_fac(x);
}
for (auto [x, y] : mp)
cout << x << " ";
cout << endl;
}

2020-2021 ACM-ICPC Brazil Subregional Programming Contest的更多相关文章

  1. (寒假GYM开黑)2018-2019 ACM-ICPC Brazil Subregional Programming Contest

    layout: post title: 2018-2019 ACM-ICPC Brazil Subregional Programming Contest author: "luowenta ...

  2. 2018-2019 ACM-ICPC Brazil Subregional Programming Contest PART (10/13)

    $$2018-2019 ACM-ICPC Brazil Subregional Programming Contest$$ \(A.Slackline\ Adventure\) \(B.Marbles ...

  3. 2019-2020 ACM-ICPC Brazil Subregional Programming Contest (11/13)

    \(2019-2020\ ACM-ICPC\ Brazil\ Subregional\ Programming\ Contest\) \(A.Artwork\) 并查集,把检测区域能在一起的检测器放在 ...

  4. 2018-2019 ACM-ICPC Brazil Subregional Programming Contest

    A:留坑 B:二维sg函数,特判边界情况 //#pragma GCC optimize(2) //#pragma GCC optimize(3) //#pragma GCC optimize(4) / ...

  5. Gym.101908 Brazil Subregional Programming Contest(寒假自训第六场)

    这几天睡眠时间都不太够,室友晚上太会折腾了,感觉有点累,所以昨天的题解也没写,看晚上能不能补起来. B . Marbles 题意:给定N组数(xi,yi),玩家轮流操作,每次玩家可以选择其中一组对其操 ...

  6. 2013-2014 ACM-ICPC Brazil Subregional Programming Contest 题解

    [题目链接] 这场比赛题面英文都好长... ... A - Zero or One 模拟. #include <bits/stdc++.h> using namespace std; in ...

  7. ACM ICPC, JUST Collegiate Programming Contest (2018) Solution

    A:Zero Array 题意:两种操作, 1 p v  将第p个位置的值改成v  2  查询最少的操作数使得所有数都变为0  操作为可以从原序列中选一个非0的数使得所有非0的数减去它,并且所有数不能 ...

  8. ACM ICPC, Amman Collegiate Programming Contest (2018) Solution

    Solution A:Careful Thief 题意:给出n个区间,每个区间的每个位置的权值都是v,然后找长度为k的区间,使得这个区间的所有位置的权值加起来最大,输出最大权值, 所有区间不重叠 思路 ...

  9. Codeforces Gym101473 A.Zero or One (2013-2014 ACM-ICPC Brazil Subregional Programming Contest)

    代码: #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #i ...

  10. Codeforces Gym101473 F.Triangles-前缀和 (2013-2014 ACM-ICPC Brazil Subregional Programming Contest)

    前缀和. 代码: 1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include< ...

随机推荐

  1. LeetCode题集-1- 两数之和

    这个题目是什么意思呢?简单来说就是在一个数组中找出两个元素,使其和为我们设定的值,并且每个元素只能用一次. 如下图具体示例: 到这里不知道你是否已经有解题思路了呢? 解法一:双层循环 我第一反应就是双 ...

  2. net core中byte数组如何高效转换为16进制字符串

    在 .NET Core 中,如何把 byte[] 转换为 16 进制字符串?你能想到哪些方法?什么方式性能最好?今天和大家分享几种转换方式. 往往在处理字符串性能问题时,首先应该想到的是怎么想办法减少 ...

  3. Vs Code, Visual Studio 2022, Angular and Live Server Running Through Https and IP Address

    前言 之前就写过 angular cli, vs code liveserver, vs 2019 iis express 10, vs code kestrel 使用 https + ip. 但写的 ...

  4. 七、Scrapy框架-案例1

    1. 豆瓣民谣Top排名爬取 1.1 构建scrapy项目 安装Scrapy库 pip install scrapy 创建Scrapy项目 通过cmd进入命令窗口,执行命令scrapy startpr ...

  5. CF1659 Codeforces Round #782 (Div. 2) 题解

    之前说过的题解,E应该不会补了(大概) A Red Versus Blue 题意非常简单,构造题.给定\(r\)个红色气球和\(b\)个蓝色气球,将它们排成一排,要求使得连续出现的最多的同色气球最少, ...

  6. 技术分享 | 徐轶韬:从MySQL5.7升级到MySQL 8.0

    在6月20日举办的[墨天轮数据库沙龙-MySQL 5.7 停服影响与应对方案]中,甲骨文MySQL解决方案首席工程师徐轶韬分享了<从MySQL5.7升级到MySQL 8.0>主题演讲,本文 ...

  7. 云原生周刊:Kubernetes 1.30 的一切新功能 | 2024.4.1

    开源项目推荐 Kubernetes scheduler simulator 该项目是一个用于模拟 Kubernetes 调度器行为的开源项目,可用于测试和评估调度器的性能和行为.它提供了一个模拟集群和 ...

  8. POI方式实现Excel表格数据导出

    Excel表格导出 1.添加pom依赖 1 <!-- office 操作工具 --> 2 <dependency> 3 <groupId>org.apache.po ...

  9. css flex布局的使用

    felx弹性布局 display:flex; 属性值 flex-direction 属性定义容器要在哪个方向上堆叠 flex 项目.默认为水平方向 row, column 值设置垂直方向.如:flex ...

  10. redis配置文件剖析

    解析配置文件 #是否在后台运行:no:不是后台运行 daemonize yes #是否开启保护模式,默认开启.要是配置里没有指定bind和密码.开启该参数后,redis只会本地进行访问,拒绝外部访问. ...