首页 > 其他分享 >省选计划(第三周)

省选计划(第三周)

时间:2023-07-17 23:24:19浏览次数:38  
标签:省选 cdot 复杂度 第三周 int 计划 que binom sum

知识回顾:

  • 巩固:概率DP,错排,组合数

  • 深入研究:组合数,后缀数组,tarjan,2-SAT

  • 简单了解/没学明白:

练题:

[ABC280F] Pay or Receive

概率 DP。

定义 \(f_i\) 为把怪物打成 i 滴血的期望攻击次数。

令 \(p=\frac{p}{100}\)。

则 \(f_i=f_{i+2} \cdot p+f_{i+1} \cdot (1-p)+1\)。

最终答案为 \(f_0\)。

复杂度 \(O(n)\)。

[ABC281G] Farthest City

一句话题意:问有多少种图满足 1 到 n 的最短路大于 1 到其他点的最短路。

容易想到将这些点分层。

定义 \(f_{i,j}\) 为考虑前 i 个点,其中有 j 个点在最外层上。

那么枚举上一层的点数,可以得到的转移方程为:

\[f_{i,j}=\sum\limits_{k=1}^{i-j}f_{i-j,k}\cdot \dbinom{n-1-i+j}{j}\cdot (2^k-1)^j \cdot 2^{\frac{j \cdot (j-1)}{2}} \]

i=n 时需要特殊处理一下。

复杂度 \(O(n^3)\)。

P5239 回忆京都

先用杨辉三角形预处理出组合数,然后二维前缀和算就行了。

\[\sum\limits_{i=1}^{m}\sum\limits_{j=1}^{n}c_{i,j}=sum_{m,n} \]

复杂度 \(O(n^2)\)

P4071 [SDOI2016]排列计数

组合数+错排。

这里的组合数比较大,需要用快速幂。

错排公式为 \(f_i=(i-1)\cdot (f_{i-1}+f_{i-2})\) 其中 \(i > 2\)。

所以最终答案为 \(\dbinom{n}{m}\cdot f_{n-m}\)。

时间复杂度 \(O(n\log n)\)。

P8321 『JROI-4』沈阳大街 2

很有意思的 DP。

首先发现如果直接暴力 DP 的话,会出现重复的问题。

我们不妨将 a 和 b 连个数组合并成 c,并记录 c 中每个元素来自哪里。

然后从大到小排序。

定义 \(f_{i,j}\) 表示前 i 个元素配 j 对的值。转移方程显然:

\[f_{i,j}=f_{i-1,j-1}\cdot c_i \cdot (num-j+1)+f_{i-1,j} \]

其中 num 为 1 到 i - 1 中与 i 的来源不一样的元素的个数。

初始化:\(f_{i,0}=1\)。

答案:\(f_{n \cdot 2,n} \cdot inv\),其中 \(inv=(!n)^{-1}\)。

复杂度 \(O(n^2)\)。

P5353 树上后缀排序

题如其名。

比较可以采用树上倍增。

现在的主要问题是可能会有相同的后缀。

于是基数排序时我们不仅要考虑前后两端的 rk 数组,还要加上上一次的 rkk 数组,即加上 id 限制后没有并列排名的数组。

剩下的就和普通的后缀排序差不多了。

复杂度 \(O(n \log n)\)。

Three strings

后缀数组拓展应用。

显然可以转化为求 LCP 的问题。

先将三个字符串拼接起来,中间用奇怪的符号隔开,跑一边后缀排序。

然后将每个位置按其 height 数组从大到小排序。

从大到小枚举 len,用并查集维护连通起来的块,对于每个块记录其分别有多少后缀在a、b、c三个字符串中。

每次合并时更新答案,即 \(cnt_1 \cdot cnt_2 \cdot cnt_3\)。

实现还是有些难度的,而且比较抽象。

复杂度 \(O(n \log n)\)。

P4782 【模板】2-SAT 问题

  • 基本模型:

给定 n 个集合,每个集合有 2 个元素。再给定若干要求,形如
“a 与 b 只能选其一” 。请选出 n 个元素,使得从每个集合选且
仅选了 1 个元素,并且满足所有要求。

n 个集合相当于 n 个布尔变量。

每个要求相当于“变量 x = T/F 与 y = T/F 只能选其一”。

原问题相当于求一组布尔变量的可行值。

  • 模型转化:

将每个布尔变量看成是两个结点 x 与 !x

x 表示变量取值为 T

!x 表示变量取值为 F

x 与 y 只能取其一,相当于两个条件关系,即两个有向边

x 成立,则 !y 必须成立,连边 x → !y

y 成立,则 !x 必须成立,连边 y → !x

原问题无解 等价于 存在 x 与 !x 属于同一个 scc

于是可以用 Tarjan 解决。

  • 构造一组可行解:

对于任意的一条有向边 \((u,v)\),必有 \(scc_i \ge scc_j\)。

多以对于每一对 x 和 !x,我们可以贪心地取编号小的。

其实就是反向边的拓扑排序。

  • 常见转化:

x 和 y 只能取其一:\((x,!y),(y,!x)\)。

x 和 y 至少满足其一:\((!x,y),(!y,x)\)。

若取 x 则必取 y:\((x,y),(!y,!x)\)。

x 必为 T:\((!x,x)\)。

P6378 [PA2010] Riddle

两种情况:

  1. x 和 y 至少选一个

  2. 如果选了 x,则一个集合内的所有元素均不能选。

可以发现,第一种情况很好处理,而第二种会使时间和空间复杂度上升至 \(O(n^2)\),需要优化建边。

可以给每个点建立两个辅助点,分别维护前缀集合和后缀集合。

然后对于每个点只需要向它的前继前缀和后继后缀两个点连边即可。

这样点数是 4n,边数是 m+4n。

于是我们就得到了一个常数巨大的 \(O(n)\) 做法。

Break Up

Tarjan + 最小割

本来想直接用 Tarjan 解决,即对于每一条,记录其子树内有多少返祖边,结果发现这玩意不好维护,于是考虑网络流。

如果 s 和 t 不连通,则直接输出 -1,可以用并查集维护。

考虑只割 1 条边的情况。

跑一遍 Tarjan 找最小的割边使得去掉后 s 和 t 不连通。

这里可以从 s 开始跑,然后判断 t 是否在桥的子树内,dfs序判断就行。

再考虑只割 2 条边的情况。

将每一条边的边权加上极大值 inf,使得求最小割时尽可能地少选边。

同时,为了防止只选一条边,我们把所有桥的边权加上 \(4 \cdot inf\)。

最后只需要判断最大流是否大于 \(3 \cdot inf\) 即可。

如果大于,输出 -1。否则 dfs 只走没有满流的边,看 s 能到达哪些点,找到两个端点状态不同的两条边就行了。

需要注意的是,如果当前的流量已经大于 \(3 \cdot inf\) 了,就要立刻退出,否则会在第 153 个点 TLE。(被坑了好久......

复杂度 \(O(n^2m)\)。

Museums Tour

可以想到拆点。

对于每个点 i,拆成 d 个,定义第 j 天的 i 号节点的编号为 \(x_{i,j}\)

那么对于一条边 \((u,v)\),可以连接 \((x_{u,j}, x_{v,j+1})\),j 表示一周中的每一天。

这个图显然有环,不能直接用最长路解决,需要缩点。

对于每个强联通分量,记录其内部有多少个开门的不同的博物馆。

然后在 DAG 上 dfs 一遍就行了。

这样会不会算重呢?答案是不会。

假设有两个点 \(x_{u,j}\),\(x_{u,j+k}\) 分别处在不同的强联通里。

如果没有直接或间接的连边,则不影响。

现在考虑会不会出现有连边的情况。

如果 \((x_{u,j},x_{u,j+k})\),则 \((x_{u,j+k},x_{u,j+k+k})\),以此类推可以得出 \((x_{u,j+(d-1)\cdot k},x_{u,j+d \cdot k})\),即 \((x_{u,j+k},x_{u,j})\),必然处在同一强联通内。

所以不存在处在不同强联通且有直接或间接连边的情况,也就不会有算重的情况。

复杂度 \((nd)\)。

[ABC283D] Scope

被这题卡了好久,气得我直接弃赛了,最后发现是数据的锅(蚌。

直接从左到右用栈模拟,也可以写成递归的形式,然后记录字母是否出现过。

时间复杂度 \(O(26n)\)。

E,F计划第四周补。

模拟赛:

省选计划

都和数学相关。

40+66+20=128 rk24。

T302381 A

没想到垂直平分线,寄了。

设原点为 \(O\),污染位置的坐标为 \(A_{1 \sim n}\)。不难发现,一个点 \(P\) 能到达当且仅当 \(\lvert OP\rvert \lt \min_ {i = 1} ^ n \{\lvert A_iP \rvert\}\)。对于每个污染位置分别考虑,\(OA_i\) 的垂直平分线会将平面划分成两个区域,其中一部分是不可达到的,计算出该直线与 \(y = m\) 的交点后可以求出可达到的范围。将所有污染位置对应的可达范围取交即为最终答案。注意特判 \(x = 0\) 的情况。

时间复杂度 \(O(n)\),空间复杂度 \(O(n)\)。

代码较短,贴一下。

#include <bits/stdc++.h>
using namespace std;
typedef double db;
int n;
db c, d, m, l = -INT_MAX, r = INT_MAX;
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		db c, d, w; scanf("%lf%lf", &c, &d);
		w = (2 * d * m - c * c - d * d) / (-2 * c); //求交点
		if (c == 0 && d >= 0 && d <= 2 * m) {
			printf("0.000\n");
			return 0;
		} if (c < 0) l = max(l, w);
		if (c > 0) r = min(r, w);
	}
	if (r == INT_MAX || l == -INT_MAX) cout << "Infinity" << endl;
	else printf("%.3lf\n", r - l);
	return 0;
}

T302382 B

设 \(f(x)\) 表示 \(b\) 进制下 \(x\) 的非零位个数,设 \(g_i = \min\{f(x) \mid x \equiv i \pmod n\}\),则答案即为 \(g_0\)。

考虑 \(b\) 进制下在 \(x\) 后增加一位 \(j\)(\(0 \le j < b\)),可以列出以下不等式:

\[g_{(i \times b + j) \bmod n} \le g_i + [j > 0] \]

这是一个差分约束模型,可以转化为 01 最短路进行计算。

根据 01 最短路的性质,每个点只会被访问一次。因此在用某个节点更新别的节点时,只需要访问未求出最短路的节点。而每个点连的出边是一段连续的区间,因此问题可以转化为,找到区间中所有未被更新的点并更新。这可以简单地用并查集维护,将每个更新过的点指向它后一个点,那么从某个点开始跳并查集就可以找到它之后第一个未被更新过的位置。

时间复杂度 \(O(n)\),空间复杂度 \(O(n)\)。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 5;
ll n, b;
int fa[N], ans[N];
vector<int> q[2];
inline int ff(int x) {
	return fa[x] == x?x:fa[x] = ff(fa[x]);
}
inline void add(int id, int x, int w) {
	if (ans[x] != -1) return;
	ans[x] = w;
	fa[ff(x)] = fa[ff(x + 1)];
	q[id].push_back(x);
}
int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> b;
	if (b >= n) {cout << 1 << endl; return 0;}
	for (int i = 0; i <= n; ++i) ans[i] = -1, fa[i] = i;
	for (int i = 1; i <= b; ++i) add(0, i, 1);
	while (!q[0].empty()) {
		for (int i = 0; i < q[0].size(); ++i) add(0, q[0][i] * b % n, ans[q[0][i]]);
		for (int i = 0; i < q[0].size(); ++i) {
			int l = (q[0][i] * b + 1) % n, r = (q[0][i] * b - 1 + b) % n, w = ans[q[0][i]] + 1;
			if (l <= r) {
				for (int j = ff(l); j <= r; j = ff(j + 1)) add(1, j, w);
			} else {
				for (int j = ff(l); j < n; j = ff(j + 1)) add(1, j, w);
				for (int j = ff(0); j <= r; j = ff(j + 1)) add(1, j, w);
			}
		}
		swap(q[0], q[1]);
		q[1].clear();
	}
	cout << ans[0] << endl;
	return 0;
}

T302383 C

巨巨巨巨好的一道题。

记:

\[\begin{cases} F(n, m) = \sum_{i = 0} ^ n \sum_{j = 0} ^ m [(i + j) \bmod 2 = 0] \binom{i}{j} \\ G(n, m) = \sum_{j = 0} ^ m \binom{n}{j} \\ H(n, m) = \sum_{i = 0} ^ n [i \bmod 2 = 0] \binom{i}{m} \end{cases} \]

考察 \((n, m) \to (n + 1, m), (n, m + 1)\) 时 \(F, G, H\) 的变化情况。可以得到:

\((n, m) \to (n + 1, m)\) 时,\(H\) 容易推出,\(G\) 可以利用 \(\binom{n}{m} = \binom{n - 1}{m} + \binom{n - 1}{m - 1}\) 得到 \(G(n + 1, m) = 2G(n, m) - \binom{n}{m}\),\(F\) 类似于 \(G\)。

\((n, m) \to (n, m + 1)\) 时,\(G\) 容易推出,\(F\) 可以利用 \(H\) 得到,只需要解决 \(H\) 如何变化的问题。

注意到 \(H(n, m + 1) = \sum_{i = 0} ^ n [i \bmod 2 = 0] \binom{i}{m + 1} = (\sum_{i = 0} ^ n [i \bmod 2 = 1] \binom{i}{m + 1} + \binom{i}{m}) + C\),其中 \(C\) 是可以用 \(O(1)\) 个组合数简单表示的内容。又 \(2H(n, m + 1) + H(n, m) = (\sum_{i = 0} ^ n \binom{i}{m + 1} + \binom{i}{m}) + C\) 亦可以用 \(O(1)\) 个组合数简单表示,所以在 \(O(1)\) 时间内能完成 \(H(n, m) \to H(n, m + 1)\) 的计算。

直接使用莫队算法,给所有 \(q\) 次询问分别按 \(\lfloor \frac{n}{B} \rfloor\) 与 \(m\) 排序,然后相邻询问之间直接暴力递推 \(n, m\) 即可。

时间复杂度 \(O(q \sqrt {\max(n, m)})\),空间复杂度 \(O(q + \max(n, m))\)。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5, mod = 998244353;
int q, pos[N];
ll ans[N], inv[N], fac[N];
ll res = 1, L = 0, R = 0, row = 1, col = 1;
struct node{
	int l, r, id;
}que[N];
bool cmp(node x, node y) {
	if (pos[x.l] == pos[y.l]) return x.r < y.r;
	return x.l < y.l;
}
ll C(int n, int m) {
	if (0 <= m && m <= n) return fac[n] * inv[n - m] % mod * inv[m] % mod; 
	return 0;
}
void addrow(int n, int m)  {
	res = (res + row) % mod;
	if ((n + m) & 1) res = (res - C(n - 1, m) + mod) % mod;
	row = (2 * row % mod - C(n - 1, m) + mod) % mod;
	if (!(n & 1)) col = (col + C(n, m)) % mod;
}
void delrow(int n, int m) {
	if (!(n & 1)) col = (col - C(n, m) + mod) % mod;
	row = (row + C(n - 1, m)) * inv[2] % mod;
	if ((n + m) & 1) res = (res + C(n - 1, m)) % mod;
	res = (res - row + mod) % mod;
}
void addcol(int n, int m) {
	row = (row + C(n, m)) % mod;
	col = (-col + mod) % mod;
	if (n & 1) col = (col + C(n + 1, m + 1)) % mod;
	else col = (col + C(n + 2, m + 1)) % mod;
	col = col * inv[2] % mod;
	if (m & 1) res = (res + C(n + 1, m + 1) - col + mod) % mod;
	else res = (res + col) % mod;
}
void delcol(int n, int m) {
	if (m & 1) res = (res - C(n + 1, m + 1) + col + mod) % mod;
	else res = (res - col + mod) % mod;
	col = col * 2 % mod;
	if (n & 1) col = (col - C(n + 1, m + 1) + mod) % mod;
	else col = (col - C(n + 2, m + 1) + mod) % mod;
	col = (-col + mod) % mod;
	row = (row - C(n, m) + mod) % mod; 
}
int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> q;
	int n = 0;
	for (int i = 1; i <= q; ++i) {
		cin >> que[i].l >> que[i].r;
		que[i].id = i;
		n = max(n, que[i].l);
	}
	n += 2;
	int len = sqrt(n);
	for (int i = 1; i <= n; ++i) pos[i] = (i - 1) / len + 1;
	sort(que + 1, que + q + 1, cmp);
	inv[0] = inv[1] = fac[0] = fac[1] = 1;
	for (int i = 2; i <= n; ++i) {
		inv[i] = (mod - mod / i) * inv[mod % i] % mod; 
		fac[i] = fac[i - 1] * i % mod;
	}
	for (int i = 2; i <= n; ++i) inv[i] = inv[i - 1] * inv[i] % mod;
	for (int i = 1; i <= q; ++i) {
		while (L < que[i].l) addrow(++L, R);
		while (R < que[i].r) addcol(L, ++R);
		while (L > que[i].l) delrow(L--, R);
		while (R > que[i].r) delcol(L, R--);
		ans[que[i].id] = res;
	}
	for (int i = 1; i <= q; ++i) cout << ans[i] << endl;
	return 0;
}

标签:省选,cdot,复杂度,第三周,int,计划,que,binom,sum
From: https://www.cnblogs.com/HQJ2007/p/17561593.html

相关文章

  • 省选计划(第二周)
    知识回顾:巩固:二分,倍增,优化DP,莫队,分数规划,网络流,二分图,贪心,set/map,KMP深入研究:分治(线段树分治),后缀数组,费用流简单了解/没学明白:线性基,边分治,数位DP,博弈论练题:[SCOI2015]国旗计划直接模拟复杂度\(O(n^2)\),显然会超时,于是考虑倍增。定义\(st_{i,j}\)表示从i这条......
  • 省选计划(第五周)
    知识回顾巩固:线段树,贪心深入研究:数论,容斥,除法分块,根号分治简单了解:lucas,prufer序列,莫比乌斯反演练题P2606[ZJOI2010]排列计数可以发现这符合小根堆的性质,把它建出来。定义\(f_u\)为以u为根的子树的方案数,转移方程为:\[f_u=\dbinom{siz_u-1}{siz_{2\cdotu}}\c......
  • 省选计划(第四周)
    第四周知识回顾巩固:2-SAT深入研究:概率与期望简单了解/没学明白:练题P3825[NOI2017]游戏很麻烦的2-SAT。如果没有x,就是个传统的问题。然后我们发现d的取值很小,考虑对于每个x枚举其类型为a还是c。为什么不枚举b呢?因为a、c已经包含b了。连边的时......
  • 省选计划 (第七周)
    练了好久CF&AT的题,现在要回归省选计划了!知识回顾巩固:二维树状数组深入了解:可持久化数据结构简单了解/没学明白:练题P3567[POI2014]KUR-Couriers主席树模板题。如果左子树的个数大于右子树的个数,则递归左子树,否则右子树。最后到达单点的时候就判断一下个数是否......
  • 省选计划(第六周)
    知识回顾巩固:Lucas,网络流,二分图深入研究:背包DP,树形DP,区间DP,状压DP。简单了解/没学明白:博弈论,插头DP练题Workingroutine读完题后以为矩阵可以相邻,费了一个小时去写...(可恶直接暴力交换复杂度是\(O(nmq)\),无法接受,考虑链表。对于每个点i,定义\(right_i\)为i的......
  • 假期计划
    为表假期好好学习之决心,特此制定一份基于大尺度宏观计划和小尺度每日时间规划的假期学习计划。·每日时间规划\(7:30\)\(ard.\)起床,洗刷,吃饭。$8:30$开始上午学习具体学习内容:1:各科假期作业(语文、数学、英语、物理、化学、地理)2:学习优先级(语文\(\rightarrow\)英语\(\r......
  • 我的投票计划
    《我的投票计划》     https://tieba.baidu.com/p/8505205758      @卡西地   ,  我看到了你发的 25楼,   现在没了, 截图在下面 。 我现在回复你,  多艾特几次会给百度服务器造成过大的负担吗 ?   吧务还要操心这个 ? ......
  • 第三周 七月十三日
    今天是当家教的第一天4,5,6,7年纪表现良好,和我保持了很好的互动性8年纪的同学是不是有点太腼腆了,怎么没有人和我说话啊???好压抑中午就我蒸的一锅米饭熟了一锅还有好多水另一锅米饭夹生我炒了炒夹生的米饭结果米饭糊了幸好我们未雨绸缪提前买了很多馒头而且家长也过来帮忙......
  • 第三周 七月十四日
    今天是当家教的第二天先说总结中午因为家长的无私奉献午饭没有出任何差错了哎你大爷还是你大爷其他年级还是表现很好我教的数学完全没有问题初二的也因为关系熟了上课也不是太压抑了都说物理数学讲的快  理科的东西太简单了完全没有难度学会了就开下一节了讲物理......
  • 第三周 七月十五日
    今天是当家教的第三天家长还是发挥稳定中午饭菜没有出现任何问题今天算了一下饭费一个月一个学生150感觉要的太少了我们每天吃饭的成本和收的钱几乎一样但是由于是家长做饭没有要人工费所以实际收费应该更高所以又草率了麻烦家长啦今天讲物理物态变化好简单啊家长......