Loading

ATC 做题记录

如果一场里出现了但是没有题解的题代表还没写或没做,否则代表太水了。

AGC and similar

AGC001

B - Mysterious Light

先反射两次形成一个平行四边形,设长为 \(A\),斜着的边长度为 \(B\),那么每次可以看成在平行四边形的某个角,不停的折射长度为 \(B\) 的线段。于是直接模拟这个过程,形成新的平行四边形 \((B, A\bmod B)\),容易发现这个过程类似欧几里得算法,所以复杂度为 \(O(\log N)\)

C - Shorten Diameter

每次只能删除叶子节点。考虑枚举直径中心,然后暴力删点即可复杂度 \(O(n^2)\)。好像可以换根 \(\tilde{O}(n)\) 做。

D - Arrays and Palindrome

对于相等的部分连一条边,最后要全连通。总共会连 \(\sum_{i=1}^{M}\lfloor\frac{a_i}{2}\rfloor\)。显然当奇数大于两个的时候就无解。然后考虑错位构造,把两个奇数放在开头和结尾,发现 \(a_1-1,a_2,a_3,...,a_{m-1},a_m+1\) 这种构造即可。

E - BBQ Hard

\(\binom{a_i+a_j+b_i+b_j}{a_i+a_j}\) 可以看成从 \((0,0)\) 走到 \((a_i+a_j,b_i+b_j)\) 的方案数。然后考虑暴力dp \(f_{i,j}\) 代表走到 \((i,j)\) 的方案数,转移从左边和下边两个方向转移即可,但是这样还是会有 \(n^2\) 个点,考虑平移一下坐标,\((-a_j,-b_j)\) 走到 \((a_i,b_i)\),这样就只有 \(O(n)\) 个起点和 \(O(n)\) 个终点,可以直接做了。重点是观察发现有 \(O(n^2)\) 个终点和 \(O(1)\) 个起点然后考虑能否折中。

F - Wide Swap

\(|p_i-p_j|=1\) 这个条件比较迷惑,又考虑到 \(i,j\) 可以任意选,于是考虑求出逆排列,然后条件即变为 \(|q_i-q_{i+1}|\ge k\),交换相邻两个,然后我们想要干的是对 \(q\) 排序。考虑最基础的冒泡排序,发现正确性是有的,然后思考能否用归并优化,实际上是可以的,把比较函数改为比较后缀min是否大于等于另一个数加 \(k\)

AGC002

D - Stamp Rally

显然先二分答案,然后建出 kruscal 重构树,倍增找到合适的点,判断这个子树大小是否满足要求。

E - Candy Piles*

牛逼题。

F - Leftmost Ball*

这么傻逼的计数怎么没想出来/fn

显然这玩意等价于任意前缀白点数不小于其他颜色种类数。于是动态规划,\(f_{i,j}\) 代表 \(i\) 个白点,\(j\) 种其他颜色的点的方案数。假设再多放一个白点,\(f_{i-1,j}\to f_{i,j}\)。假设再多放一种其他颜色的点,枚举下一个空位放哪种颜色,把这种颜色所有点插入剩下的空位里。这样显然保证任意前缀白点数不小于其他颜色种类数。

AGC003

D - Anticube

对每个数求一下他的补集,取个 \(\max\) 就好了。

E - Sequential operations on Sequence

注意到我们只关心最后是什么,考虑倒推,显然前面比它大的都没用,所以只用维护一个单调递减的序列即可。

然后变为对每一段求答案,而这这一段显然是若干段前一段加上最后一点取模的部分,二分出第一个小于的地方,这样取模后会减半,总复杂度 \(O(n\log^2n)\)

F - Fraction of Fractal*

称在右边放一个相同的图形,跨越左右的的连通块个数为 \(a\),上下为 \(b\)。黑色块数为 \(cnt\)。显然若 \(a=0,b=0\),就是 \(cnt^{k-1}\)\(a=1,b=1\),答案就是 \(1\)。否则不妨设 \(a=1,b=0\)。记黑色左右相邻的个数为 \(c\)。有递推式:\(f_{k}=cnt\times f_{k-1}-c\times s_{k-1}\),其中 \(s_{k}\) 表示 \(k\) 级分型的左右相邻连通块数。\(s_1=1\)。这东西也可以递推 \(s_{n}=s_{n-1}\times u\),其中 \(u\) 是初始图形右边拼起来时相邻的块数。

AGC004

E - Salvage Robots*

dp?不会。

把机器人动变成出口动,出口往上等价于机器人往下,其他同理。记录出口最多往左往右往上往下分别 \(l,r,u,d\) 个格子。那这样有一些位置是一定选不到了,有一些位置,如果在过程中能选,是已经被选完了。\(f[l][r][u][d]\) 代表此时最大的答案,考虑扩展一维,可以发现可以被选的是一段区间,直接转移即可。细节较多。

AGC005

D - ~K Perm Counting

容斥,钦定 \(k\) 个位置满足 \(|p_i-i|=k\)。构造一个值域和位置的二分图,显然满足条件的位置是一个匹配。这个二分图是若干条链,直接 dp 即可。

E - Sugigma: The Showdown

先观察什么时候是 \(-1\),一个必要条件即如果 A 树存在一条边 \(u,v\) 满足其在 B 树上的距离 \(\ge 3\)。那么假设先手并没有找到这样一条边,那么此时先手所在节点一定在后手子树内,于是先手走不到在 A 树上,距离后手在 B 树上更远节点,只能走到更近的节点,在这一范围内查找是否会出现 \(-1\) 即可,如果都不出现,就是这个最大距离的两倍。

F - Many Easy Problems

考虑一条边的贡献,当所有点都在这条边的一侧时才没有贡献,所以对 \(i\) 的贡献应该是 \(\binom{n}{i}-\binom{sz}{i}-\binom{n-sz}{i}\)。记录每个 \(sz\) 出现的次数即可。那么每个 \(i\) 的答案大概可以写成 \(n\binom{n}{i}-\sum_{j\ge i}c_j\binom{j}{i}\)。然后直接开卷。

AGC006

C

D

E

F - Blackout

\((x,y)\) 看作是一条有向边 \(x\to y\),显然可以在一个弱连通块里考虑。

将这个若联通块三染色,\(u\to v,c_v=c_u+1\pmod 3\)。然后开始分类讨论。

  1. 如果存在三染色并且有的颜色没有用到,那么一定不能连新的边。

  2. 如果存在三染色并且每种颜色都用到了那么两两颜色之间都可以连。

  3. 如果不存在三染色,那么没对点之间都会连边,包括自环。

对于第三点,可以考虑一定会有一个环,那么就可以对于环上的一个点直接跳过它。

于是直接做即可,复杂度 \(O(n+m)\)

AGC007

C

看到题目感觉无从下手,不像dp也不像直接计数。手玩一下发现每次操作完之后球与洞之间的期望距离竟然也是等差数列而且与之前的 \(d,x\) 有关系。多玩几组即可发现这种关系 \(d'=d+\frac{2d+5x}{2n},x'=x+\frac{2x}{n}\)。然后每一次期望距离其实就是中位数 \(d+\frac{(2n-1)x}{2}\),到这里就可以切掉这道题了,时间复杂度 \(O(n)\)

肯定有人想知道为什么?然后我发现官方题解也没说为什么

让我们来yy一下,先考虑首项的变化,即第一个球入洞,两种情况分别为 \(d+2x,3d+3x\),其余 \((2n-2)\) 种情况均为 \(d\),所以易知 \(d'=\frac{(2n-2)d+4d+5x}{2n}=d+\frac{2d+5x}{2n}\)

然后公差同理,两种情况 \(\frac{2nx+x+3x}{2n}=x+\frac{2x}{n}\)

也可以根据总和来推公差。

AGC008

A

B

C

D. K-th K

我们按 x 从大到小考虑,要在前面放数,每次尽量往后放。然后再从前往后考虑,显然先把前面的空补齐,然后贪心尽量往前放即可。

E

F

AGC014

A

B

C

D

E. Blue and Red Tree

正推明显比反推好推。考虑先把红边对应到蓝边的路径上,然后每次必然是找到一条只经过一次的边,把经过这条边的路径找出来删掉。不断做这个过程直到所有路径删除,如果中间有一次不止一条边就是NO。

思路简单,代码细节不是很好想。考虑在每个节点记录4个信息,区间mn,加法标记,完全经过这个区间的路径数量,完全经过这个区间的路径编号和。然后每次找只需找到一个完全经过某个区间的路径数量为1 的区间即可。

F

AGC018

A

B

F

首先容易发现每个点的奇偶性与其儿子个数有关,那么先求出一定 impossible 的情况。接下来我们说明可以在其余情况只用 \(\{-1,0,1\}\) 构造一个解。

我们发现我们的判定于度数的奇偶性有关,这就引导我们去思考欧拉路。度数为奇数的点需要是 \(1,-1\),偶数是 \(0\) 即可。为了解决根的问题我们新建一个虚根连到两个根即可。然后奇数度点与另一棵树的点连边。此时所有点的度数均为偶数,求一个欧拉回路。如果一条连接两棵树的边是从 A 到 B 的那么赋值 \(1\) 否则 \(-1\)

下面来证明一下正确性。

AGC020

A

B

C

D

E

F. Arcs on a Circle

给定一个长度为 \(C\) 的圆和 \(n\) 条弧,第 \(i\) 条弧的长度为 \(l_i\),随机放置在圆上,求覆盖整个圆的概率。\(1\le n\le 6,1\le C\le 50\)

标签:状压dp,概率

先考虑选定一条弧作为起点(一定要选最长的),然后我们发现相对位置关系只考虑小数的相对大小关系,这个可以暴力枚举。将每个区间 \([i,i+1]\) 分成 \(N\) 份,在 \([0,NC]\cup \Z\) 的范围内选整点,使用状压dp,\(f[i,j,s]\) 左端点坐标不大于 \(i\) 的已经放完,最远到 \(j\) 包含 \(S\) 范围内圆弧。转移从选与不选考虑即可。

AGC021

A

B - Holes

题目的范围即为告诉你这是个平面,所以如果到某个点的范围是有限的话就是 \(0\) 了。那么有值的点一定是在凸包上的,多点共线时在中间的点有一维是有限长度的,所以共线也是零。接下来考虑怎么求。对于一条线段肯定是从中垂线切开,那么把一个点连的两条线段的中垂线求一下交点,算出角度,可以近似成在这个圆的占比,就求出来了。

C

D

E

F

AGC023

A

B

C

D

E

F - 01 on Tree

考虑解决一个更强的问题:每个节点是一个非空的 \(01\) 串。

\(a_i\) 是节点 \(i\)\(0\) 的个数,\(b_i\) 是节点 \(i\)\(1\) 的个数,然后考虑将一个节点合并到它的父亲上。可以证明我们每次合并 \(c_i=\frac{a_i}{b_i}\) 最大的何其父亲合并。为什么是这样的?考虑最后的排列是 \(p\),然后交换 \(p_i,p_{i+1}\),此时有 \(b_i\times a_{i+1}\ge a_i\times b_{i+1}\Rightarrow \frac{a_i}{b_i} \le \frac{a_{i+1}}{b_{i+1}}\)。也就是说 \(c_i\) 越大,放在前面越好。用单调队列来模拟这个过程即可。

AGC026

A - Irreversible operation

显然贪心地看,尽量把前面的 W 放到前面去,那么每个 W 的贡献就是前面 B 的个数。

B - Powers of two

有一个比较明显的贪心:找到一组就一直选。很可惜,按任意顺序这样做是错的,但是,我们现在这个 \(2\) 的次幂越大越不好搞,所以贪心地从最大的次幂倒着来搞就可以过了。

C - Lexicographic constraints

贪心地去找答案不简单,但是可以判断一个答案 \(m\) 可不可行。我们可以把字符串看成一个 \(m\) 进制数,每个位置的值 \(\in [0,m-1]\)。然后对于 \(A_i> A_{i-1}\),直接再后面补 \(0\) 即可,否则在第 \(A_i\) 个位置加一,前面复制即可。注意还得处理进位,如果第一位也要进位那么 \(m\) 就不可行。思考这样做的复杂度,暴力是 \(O(V)\) 的,但是我们只用维护不是 \(0\) 的位置,每次操作最多多一个不是 \(0\) 的位置,所以这样的位置是 \(O(n)\) 的。最后发现答案具有可二分性,于是二分答案即可。复杂度 \(O(n\log^2(n))\)

D - Grid game

一个比较显然的结论是 Takahashi 不会停下来不走,这样 Aoki 也不走游戏就结束了。那么 Aoki 的策略就是走到一个位置 \((x,y)\) 使得 \((x+1,y)\) 是个障碍物,这样这一步他不走,下一步 Takahashi 也不能走,游戏就结束了。于是我们从小到大扫一遍 x 坐标,思考当前位置 Aoki 可以走到哪些 y 坐标上。容易发现这是一个前缀 \([1,A]\),考虑维护这个 \(A\),对于每个 \(x\) 求出最小的障碍物的位置 \(y\),如果 \(y>A+1\),那么显然可以往上走一步,如果 \(y=A+1\) 那么不能动,如果 \(y<=A\),那么在 \(x-1\) 时游戏就可以结束了。

E - Wandering TKHS

对于一个点 \(u\),在其到根的过程中是否会经过 \(v\),设 \(w=lca(u,v)\),显然需要先到 \(w\) 再到 \(v\)。那么如果 \(w\to 1\) 的最大值比 \(w\to v\) 的最大值大的话,就可以到 \(v\)。于是求一下 \(v\to 1\) 的最大值 \(z\),所有 \(z\) 的包含 \(v\) 的儿子的子树里的 \(u\) 的答案都加 \(1\)。那 \(z\) 子树里的其他部分有没有可能被贡献到?显然是有的,只要次大值在 \(z\to 1\) 的路径上即可。于是变成了子树加,做个差分即可 \(O(n)\)

F - Construction of a tree

AGC027

A

B

C

D

E

F. Grafting

枚举一个位置作为第一变换,然后以他为根,找到需要变父亲的点,他要比他A树上的父亲早断,比B树上的父亲晚断,于是出现有一张有向图,判断它是不是DAG即可。

AGC030

C - Coloring Torus

妙妙构造题,但感觉没有铜牌题的难度。

首先根据样例提示不难想到每一行填一种数字的方法。同理没一斜线上也可以填一种数字。我们又发现 \(K=2N\),于是考虑交错填。如果采用第一种填法,对于相同颜色会有影响,而第二种显然就不会了(因为相邻的会变恰好一个)。于是我们还得到了一种套路:这种相邻考斜线,四个对角那种再考虑一整行。

AGC034

A

B

C

D

D - Manhattan Max Matching

拆曼哈顿距离的另一个套路,可以拆成几个东西的max,然后建立4个新点,分别连边表示4种情况,然后��最大费用最大流即可。

F

AGC035

A

B

C - Skolem XOR Tree

题解

D

E

F

AGC036

E - ABC String

先把相邻的相同的缩起来,然后数个数,不妨设 \(cnt_a<cnt_b<cnt_c\),盲猜上界 \(3cnt_a\) 大概可以达到,先找到 \(ACB,BCA\) 这种把 \(C\) 删掉,如果没删完就找 \(AC,CA\) 这种删掉,然后必然出现 \(cnt_b=cnt_c\),接下来删 \(BC,CB\),但是注意不能有 \(ABCA,ACBA\) 这种,使用链表维护即可。

AGC037

E - Reversing and Concatenating

显然只会出现存在的字符,所以考虑把最小的移到开头。然后又因为是反转,所以我们不妨把最小的挪到末尾然后长度就可以倍增了。所以如果 \(2^{K}\) 比较大的时候直接这么做就行了。

我们再给出一个更具体的贪心,除了最后一次操作是选字典序最小的,其余全部选倒序字典序最小的,这样就可以在拼起来的时候将最小子串倍增了。

AGC038

C - LCMs

我能说这是原题吗?

最小公倍数之和

发现题目所求的式子即为 \(\frac{1}{2}\times(\sum_{i=1}^n\sum_{j=1}^nlcm(a_i,a_j)-\sum_{i=1}^na_i)\)

然后按照上面的式子做即可。

F - Two Permutations

这道最小割好妙啊。

我们先发现一个排列的性质:对于排列上的每个环,如果其中一个点转了,为了满足排列的性质,这个环上的点都会转。

那么我们把每个环所成一个点(因为这里的点要不是全部转,要不全部等于 \(i\)),那么我们搞两个集合 \(S,T\),表示如果 \(p\) 不旋转就放入 \(S\)\(q\) 不旋转就放入 \(T\),中间建边,我们让最小割数为 \(A_i=B_i\) 的数,从源点 \(S\) 连向 \(p\) 点的边表示 \(p\) 不转,从 \(q\) 点连向汇点 \(t\) 的边表示 \(q\) 不转,\(q\)\(s\) 联通代表转, \(p\)\(t\) 连通代表转。接下来分类讨论:

  1. 如果 \(p_i=q_i=i\) 那么无论转不转都会产生贡献,直接减一

  2. 如果 \(p_i=i,q_i\neq i\),那么如果 \(q\) 不旋转就会产生贡献,连向 \(T\) 一条边。

  3. 如果 \(p_i\neq i,q_i=i\),那么如果 \(p\) 不旋转就会产生贡献,\(S\) 连向其一条边。

  4. 如果 \(p_i\neq q_i\),那么都不旋转才会产生贡献,从 \(q\) 连向 \(p\) 一条边(q 连向了 t,s 连向了 p)。

  5. 如果 \(p_i=q_i\) 那么转不转都会产生贡献,\(p\rightarrow q,q\rightarrow p\)

最后两种情况为什么是在之间连边?因为这样可以割一条边,使得不连通(相当于割两条边,这样会产生冗余操作)

#include <bits/stdc++.h>
using namespace std;
template <typename T>
void read(T &num) {
	T flag = 1;
	char ch = getchar();
	for (; '0' > ch || ch > '9'; ch = getchar())
		if (ch == '-') 
			flag = -1;
	for (num = 0; '0' <= ch && ch <= '9'; ch = getchar())
		num = num * 10 + ch - '0';
	num *= flag;
}
struct node{
	int pre, to, val;
}edge[2000005];
int head[200010], tot = 1;
void add_edge(int u, int v, int l) {
	edge[++tot] = node{head[u], v, l};
	head[u] = tot;
}
int m, n;
int S, T;
int d[200010], cur[200010];
bool bfs() {
	for (int i = 0; i <= T; i++) d[i] = 0, cur[i] = head[i];
	queue<int> q;
	q.push(S);
	d[S] = 1;
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		for (int i = head[x]; i; i = edge[i].pre) {
			int y = edge[i].to;
			if (edge[i].val > 0 && !d[y]) {
				d[y] = d[x] + 1;
				q.push(y);
			}
		}
	}
	return d[T];
}
int dinic(int x, int flow) {
	if (x == T || flow == 0) return flow;
	int rest = flow;
	for (int &i = cur[x]; i; i = edge[i].pre) {
		int y = edge[i].to;
		if (edge[i].val > 0 && rest > 0 && d[y] == d[x] + 1) {
			int k = dinic(y, min(edge[i].val, rest));
			if (!k) d[y] = 0;
			edge[i].val -= k;
			edge[i ^ 1].val += k;
			rest -= k;
			if (!rest) break;
		}
	}
	return flow - rest;
}
int p[100005], q[100005];
int idp[100005], idq[100005], cnt;
int main() {
	read(n);
	for (int i = 1; i <= n; i++) {
		read(p[i]);
		p[i]++;
	}
	for (int i = 1; i <= n; i++) {
		read(q[i]);
		q[i]++; 
	}
	cnt = 0;
	for (int i = 1; i <= n; i++) {
		if (!idp[i]) {
			cnt++;
			int now = p[i];
			idp[i] = cnt;
			while (now != i) {
				idp[now] = cnt;
				now = p[now];
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		if (!idq[i]) {
			cnt++;
			int now = q[i];
			idq[i] = cnt;
			while (now != i) {
				idq[now] = cnt;
				now = q[now];
			}
		}
	}
	cerr<<"zcr\n";
	int total = n;
	S = 0, T = cnt + 1;
	for (int i = 1; i <= n; i++) {
		if (p[i] == q[i] && p[i] == i) {
			total--;
		} else if (p[i] == i && q[i] != i) {
			add_edge(idq[i], T, 1);
			add_edge(T, idq[i], 0);
		} else if (p[i] != i && q[i] == i) {
			add_edge(S, idp[i], 1);
			add_edge(idp[i], S, 0);
		} else if (p[i] == q[i]) {
			add_edge(idp[i], idq[i], 1);
			add_edge(idq[i], idp[i], 0);
			add_edge(idp[i], idq[i], 0);
			add_edge(idq[i], idp[i], 1);
		} else {
			add_edge(idp[i], idq[i], 0);
			add_edge(idq[i], idp[i], 1);
		}
	}
	int flow, ans = 0;
	while (bfs()) {
		while ((flow = dinic(S, 0x7f7f7f7f)) > 0) {
			ans += flow;
		}
	}
	printf("%d", total - ans);
	return 0;
}

AGC041

AGC047

Integer Product「AGC 047A」

本场送分题,但我还是被卡了好久,wtcl。

因为所有数小数点后最多9位,所以不妨先把所有数搞成带分数(约分后)的形式。

可以分成 \(4\) 类讨论:

1,两个整数

这种情况乘积肯定是整数,我们只需统计每个整数前有多少个整数即可。

2,一个分数,一个整数

这得满足前面分数的分母是后面整数的因数,这也很好统计,\(O(\sqrt{n})\) 枚举一下因数,然后再搞个桶统计一下每个分母有几个对应的分数。

3,一个整数,一个分数

这样和2其实一样,从后往前再统计一遍就行。

4,两个分数

这种情况比较烦,但由于这道题的性质还是可做的。

注意所有分母只有可能是 \(2^a5^b\) 的形式,又要求乘起来是整数,所以分母必然不会即有\(2\)又有\(5\),因为这样就得有一个既有\(2\)又有\(5\)的分子,而这样的分子显然是不存在的,除非是个整数,而我们又只讨论分数的情况。

所以对于一个分母为 \(2^a\) 的分数(\(5^b\)同理,这里就不讲了),然后我们枚举分子的\(5^t\)的约数,我们统计一下以\(5^t\)为分母的分子是\(2^a\)的倍数的有多少个加上就行。在求的时候其实就可以统计,以 \(2^a\) 为分母的分子是\(5^t\)的倍数其实就可以加 \(1\)

时间复杂度:对于每个整数会有 \(\sqrt{a}\) 的复杂度,而对于每个分数只有 \(\log{a}\) 的复杂度,瓶颈在整数,而整数最大才\(10^4\),所以不会超时。用了map,常数可能会大一点。

做法好麻烦,wtcl,如果有更简单的做法可以来教我qwq。

First Second「AGC 047B」

这题也是可做的,而且我觉得较A还更简单一点。

我们不妨先求一个字符串 \(s\) 前有几个可以变成 \(s\),然后再倒过来求一遍一样的东西即可。

有一个很妙的性质,除了字符串 \(s\) 的第一位其它位都必须紧贴前面的字符串的最后面,这很好理解,如果有一个不是那其后面那个空出来的就无法消掉。

那我们可以把所有前面的字符串反向建trie树,然后反过来在trie树上跑,如果不是第一位就走当前为,如果是第一位就随便走,如果走了当前为就返回。

可这样复杂度不对,考虑优化。对于每个trie树上的节点记录一下这个节点开始有几个字符串在前面有某个字符,然后如果是第一位就直接加上。那怎么维护呢,其实很简单,我们把插入边成递归插入,统计在回溯的时候记录一下当前哪些字符出现了,如果出现了某个字符回溯时就更新。

蒟蒻sb没看到字符串各不相同,所以还搞了个map去重。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 200010;
ll n;
ll a[N], b[N], c[N];
char s[20];
map<ll, ll> mp1, mp2;
map<ll, ll> dazhao[2][64];
ll ans, cnt, ans2, ans3;
ll gcd(ll x, ll y) {
	return y == 0 ? x : gcd(y, x % y);
}
int main() {
	scanf("%lld", &n);
	for (ll i = 1; i <= n; i++) {
		c[i] = 1;
		scanf("%s", s + 1);
		ll len = strlen(s + 1), flag = 0;
		for (ll j = 1; j <= len; j++) {
			if (s[j] == '.') {
				flag = 1;
			} else {
				if (flag) {
					b[i] = b[i] * 10 + s[j] - '0';
					c[i] *= 10;
				} else {
					a[i] = a[i] * 10 + s[j] - '0';
				}
			}
		}
		ll g = gcd(b[i], c[i]);
		b[i] /= g;
		c[i] /= g;
	}
	for (ll i = 1; i <= n; i++) {
		if (b[i] == 0) {
			ans += cnt;
			cnt++;
			for (ll j = 1; j * j <= a[i]; j++) {
				if (a[i] % j == 0) {
					ans += mp1[j];
					if (a[i] / j != j) ans += mp1[a[i] / j];
				}
			}
		} else {
			ll num = a[i] * c[i] + b[i];
			if (c[i] % 2 == 0 && c[i] % 5 != 0) {
				ll tmp = c[i], cr = 0;
				while (tmp % 2 == 0) {
					cr++;
					tmp /= 2;
				}
				for (ll j = 5, t = 1; j <= num; j *= 5, t++) {
					if (num % j == 0) {
						ans += dazhao[1][t][cr];//以5^t为分母的分子是2^cr的倍数的有多少个 
						dazhao[0][cr][t]++;//以2^cr为分母的分子是5^t的倍数的加一个 
					} else {
						break;
					}
				}
			} else if (c[i] % 2 != 0 && c[i] % 5 == 0) {
				ll tmp = c[i], cr = 0;
				while (tmp % 5 == 0) {
					cr++;
					tmp /= 5;
				}
				for (ll j = 2, t = 1; j <= num; j *= 2, t++) {
					if (num % j == 0) {
						ans += dazhao[0][t][cr];//以2^t为分母的分子是5^cr的倍数的有多少个 
						dazhao[1][cr][t]++;//以5^cr为分母的分子是2^t的倍数的加一个 
					} else {
						break;
					}
				}
			}
			mp1[c[i]]++;
		}
	}
	for (ll i = n; i >= 1; i--) {
		if (b[i] == 0) {
			for (ll j = 1; j * j <= a[i]; j++) {
				if (a[i] % j == 0) {
					ans += mp2[j];
					if (a[i] / j != j) ans += mp2[a[i] / j];
				}
			}
		} else {
			mp2[c[i]]++;
		}
	}
	printf("%lld\n", ans);
	return 0;
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1000010;
ll n;
ll ans;
ll trie[N][26], quick[N][26], tot = 1;
ll sum[N], cnt[26];//子树和 
char s[N];
string t[200010];
map<string, int> mp;
void get_ans(ll p, ll len) {
	if (len == 1) {
		//可以不走当前字符 
		ans += quick[p][s[len] - 'a'];
		return;
	} else {
		//不可不走当前字符 
		if (trie[p][s[len] - 'a']) {
			get_ans(trie[p][s[len] - 'a'], len - 1);
		}
	}
}
void insert(int p, int len) {
	sum[p]++;
	if (!len) return;
	int k = s[len] - 'a';
	if (!trie[p][k]) {
		trie[p][k] = ++tot;
	}
	insert(trie[p][k], len - 1);
	cnt[k]++;
	for (int i = 0; i < 26; i++) {
		if (cnt[i]) {
			quick[p][i]++;
		}
	}
}
int main() {
	scanf("%lld", &n);
	for (ll i = 1; i <= n; i++) {
		cin >> t[i];
		for (int j = 0; j < t[i].length(); j++) {
			s[j + 1] = t[i][j];
		}
		ll len = t[i].length();
		get_ans(1, len);
		memset(cnt, 0, sizeof(cnt));
		insert(1, len);
	}
	memset(trie, 0, sizeof(trie));
	memset(quick, 0, sizeof(quick));
	memset(sum, 0, sizeof(sum));
	tot = 1;
	for (int i = n; i >= 1; i--) {
		for (int j = 0; j < t[i].length(); j++) {
			s[j + 1] = t[i][j];
		}
		ans -= mp[t[i]];
		ll len = t[i].length();
		get_ans(1, len);
		memset(cnt, 0, sizeof(cnt));
		insert(1, len);
		mp[t[i]]++;
	}
	printf("%lld", ans);
	return 0;
}

ARC and similar

ARC072

F. Dam

我们发现那个公式可以扩展到 \(n\) 个,枚举最后一段没有倒水,\(dp_i*L=dp_j*(L-sv_i+sv_j)+svt_i-svt_j\),然后cdq斜率优化。

其实还可以贪心,如果后面比前面热,一定是前面先到后面再加,否则是后面先加前面再到,所以维护一个单调队列即可。

ARC075

D - Mirrored

小清新输入输出题

设最后的数 \(n\) 的位数为 \(m\)\(a_i=n/10^i \bmod 10\)。反转后的答案是 \(\sum\limits_{i=0}^{\lceil\frac{m}{2}\rceil}(10^{m-i}-10^i)(a_{i}-a_{m-i})=\sum\limits_{i=0}^{\lceil\frac{m}{2}\rceil}\overline{99\dots900\dots0}\cdot(a_{i}-a_{m-i})\),有 \(i\)\(0\)\(m-2i\)\(9\)。于是我们发现这一定是一个 \(9\) 的倍数,我们不妨直接除以一个 \(9\)

\[\sum\limits_{i=0}^{\lceil\frac{m}{2}\rceil}\overline{11\dots100\dots0}\cdot(a_{i}-a_{m-i}) \]

然后我们又发现个位数之和 \(a_{m-i}-a_i\) 有关。于是个位就确定了而且只有两种可能,暴力枚举即可。而后面都有公因数 \(10\),一起除掉即可。如果最后都算完发现数变成了 \(0\) 就合法。复杂度 \(O(m2^{m})\)\(m\) 是枚举的位数。 根据尝试知道 \(n\) 是个 longlong 范围内的数,所以 \(m\) 枚举到 \(18\) 即可。

代码也非常小清新

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll D, ans, m, pw[30], a, b;
void dfs(int i, ll num, ll now) {
	if (i > m / 2) { ans += num * (now == 0); return; }
	a = (now % 10 + 10) % 10, b = m - 2 * i;
	if ((now - (a * pw[b])) % 10 == 0)
		dfs(i + 1, i == 0 ? num * (10 - a - 1) : num * (10 - a), (now - (a * pw[m - 2 * i])) / 10);
	if ((now - (a - 10) * pw[b]) % 10 == 0)
		dfs(i + 1, num * a, (now - (a - 10) * pw[m - 2 * i]) / 10);
}
int main() {
	for (int i = 1; i <= 18; i++) pw[i] = pw[i - 1] * 10 + 1;
	cin >> D;
	if (D % 9) { puts("0"); return 0; }
	for (m = 1; m <= 18; m++) dfs(0, 1, D / 9);
	cout << ans << endl;
	return 0;
}

ARC077

F. SS

神仙结论题

神仙字符串结论题。

我们先进行一次操作变成一个 \(SS\) 串,然后考虑 \(S\) 的循环节 \(G\)

  1. \(G\)\(S\) 的整循环节

那么这个串会变成 \(SGSG\)\(SGGSGG\),...,\(SGGGG...SGGGG....\),由于是无限长我们只需考虑前半部分即可。这个直接求就行。

  1. \(G\) 不是 \(S\) 的整循环节

其实只是上一部分的扩展。

还是只用考虑前半部分,而前半部分的情况是 \(S_i=S_{i-1}+S_{i-2}\),也就是 \(S\)\(SG\)\(SGS\)\(SGSSG\)(其实样例已经给了提示)。

接下来我们来证明:

该结论等价于证明 \(SG\) 的最小循环节为 \(S\),且不会出现 \(G\) 变成整循环节的情况。设 \(SG=T\)

我们假设存在一个循环节 \(p\)\(S\) 更短,那么 \(p\) 也是 \(S\) 的循环节,所以 \(T[i]=T[i-p]\) 由于 \(G\)\(S\) 的前缀,所以 \(T[i]=T[i-|S|]\),又因为 \(G\) 是周期,所以 \(i-p\equiv i-|S|\pmod |G|\)\(|p|\equiv |S|\pmod |G|\),由于弱周期引理,\(S\) 有新的周期 \(\gcd(|p|,|G|)\),所以 \(p\)\(|G|\) 的倍数,所以 \(|S|\bmod |G|=0\) 矛盾!

于是我们直接暴力模拟即可。

ARC084

F - XorShift

不妨把这些数看成 \(\text{F}_2\) 下的多项式,两种操作可以定义为加法和 \(\times x\)。于是我们可以找到 \(\gcd A_i=D\),显然最后的答案是 \(D\) 的倍数。多项式 gcd 可以通过辗转相除法实现。然后所求极为 \(KD\le M\),类似数位dp,钦定前 \(k\) 位,如果 \(k>deg(D)\) 且第 \(k\) 位为 \(1\)\([deg(D),k-1]\) 这些位置可以随便选,否则 \(k\) 比较小的时候 \(\mod D\) 可以得到唯一值,那么直接多项式取模然后判断即可。

ARC087

F - Squirrel Migration

根据套路选重心会得到最大的距离,当这棵树有两个重心答案就是 \({(\frac{n}{2})!}^2\)。考虑只有一个重心,那么就是把子树拉出来,然后要求每个 \((i,p_i)\) 不在同一课子树内。我们设 \(g_i\) 代表钦定有 \(i\)\((i,p_i)\) 在同一棵子树内,\(f_i\) 代表恰好有 \(i\) 个,那么 \(g_i=\sum_{j\ge i}\binom{j}{i}f_j\),二项式反演得 \(f_i=\sum_{j\ge i}(-1)^{j-i}\binom{j}{i}g_j\)。答案即为 \(f_0\)

考虑 \(g_i\) 怎么求。考虑 \(dp[i,j]\) 代表前 \(i\) 棵子树有 \(j\) 对被钦定了,转移考虑枚举第 \(i\) 棵子树有几对被钦定了。

ARC091

C - Flip,Flip, and Flip......

只需要考虑每个位置被翻了几次,特判只有一行或一列,否则边界都会被翻偶数次等于没变,中间都会被翻奇数次,所以答案是 \((n-2)(m-2)\)

D - Remainder Reminder

枚举 \(b\) 以及其倍数,\(a\) 只能在 \([bx+k,b(x+1)-1]\) 内,注意边界。

E - LISDL

LIS 与 LDS 最多只有一个交点,所以 \(A+B\) 的上界是 \(N+1\),然后手玩一下发现如果最后 A 个上升,然后 B-1 个下降,然后 A-1 个上升...... 这样是最优的,最后判一下如果这样都不行就无解了。

F - Strange Nim

神仙打表题。显然的求sg函数。查看题解打出表后发现如下规律:

\[sg(n)= \begin{cases} 0,n<k\\ \frac{n}{k},n\%k==0\\ sg(n-\lfloor\frac{n}{k}\rfloor-1),otherwise\\ \end{cases} \]

然后我们考虑对相同的一段 \(\lfloor\frac{n}{k}\rfloor\) 一起算,设这个值为 \(x\),考虑有 \(y\) 段,那么需要满足 \(n-y(x+1)\ge kx \Rightarrow y\le \lfloor\frac{n-kx}{x+1}\rfloor\)

考虑时间复杂度,当 \(k\le \sqrt n\)\(n\) 每次都会减少 \(\sqrt n\),所以是 \(O(\sqrt n)\) 的;否则 \(x\) 每次减少 \(1\),而 \(x\)\(O(\sqrt{n})\) 的所以总复杂度也是 \(O(\sqrt{n})\) 的。

ARC133

放一个题解链接

ABC and similar

ABC169

F - Knapsack for All Subsets

这算是一道非常明显的背包题。

我们可以考虑算出所有可以凑数s的集合,然后再看看它属于多少个集合。

\(f[i][j]\)代表选\(i\)个数,和为\(j\)的方案数,则答案为\(\sum_{i=1}^{n}f[i][s] \times 2^{n-i}\)

有转移方程:\(f[i][j]+=f[i-1][j-a[i]]\)

但是这么dp的复杂度为\(O(N^2 \times S)\),显然爆炸,考虑优化。

考虑最后答案的式子,设\(g[j]=\sum_{i=1}^{n}f[i][j] \times 2^{n-i}\)

我们再来看看f的转移方程,我们发现有\(g[j]+=g[j-a[i]]/2\),然后这道题就做完了。

貌似这场比较简单。

A - Equal Hamming Distances

显然对于 \(s_i=t_i\) 的位置不会影响汉明距离的差,所以只需考虑 \(s_i\ne t_i\) 的位置的奇偶性。只有偶数时有解且要对半分。然后从前往后贪心,选择能选的较小值即可。

B - A < AP

套路计数题,考虑枚举一个lcp。假设lcp长度为 \(i-1\),那么假设 \(i,P_i\) 可以填不同的颜色,则共有 \(\sum\limits_{i=1}^{M}M-i=\frac{M(M-1)}{2}\) 种方法。然后考虑扩展lcp为 \(i\),即 \(i,P_i\) 得是同一种颜色,用一个并查集维护,在一个集合内的点颜色都相同。

C - 01 Game

显然是对于没两个有值的位置之间求sg函数再异或起来,打一个表,可以发现如果两边值相同的时候sg=1,否则sg=0。如果只有一边有值的话sg是空位数,如果两边都没有值的话sg是空位数模 \(2\)

证明好像手玩一下,分类讨论即可。

D - Binary Representations and Queries

你发现这个操作是一个线性变化,且操作应该按位独立(即每一位的顺序是无所谓的)。考虑合并同一位的两次操作,其实相当于做一个 \(\begin{bmatrix}1&0\\1&1\end{bmatrix}\) 的变换或者 \(\begin{bmatrix}1&1\\0&1\end{bmatrix}\)。于是直接维护,最后对每一位做一遍最后的变化即可。

E - Keep Being Substring

首先如果两者最长公共子串长度为 \(m>0\),那么答案不大于 \(P+Q-2*m\)

考虑没有公共子串的情况,我们需要一步一步挪到有公共子串的情况,而这其实相当于从一个数字 \(x\) 挪到 \(y\)。跑一个多源最短路就好了。考虑求出最短路是 \(d\),它应该怎么贡献到答案。显然它挪的过程应该是前进一步,然后把删掉后面。那么贡献就是 \(2d+P+Q-2\)。取一下 \(\min\),你就过了。

F - RGB Card Game

好牛逼啊,这种纯结论题感觉真无从下手啊,除了暴力+手玩好像没啥其他方法。

考虑处理出 \(A,B,C\) 分别表示先手比后手少的数量,后手比先手少的数量,和相同的数量。数量指的是颜色数。同时处理出 \(SA,SB,SC\) 代表这些颜色中,较小的一方的数量的和。

  • \(C=0\)

那毫无疑问,如果 \(SA\le SB\) 那先手胜,否则后手胜。

  • \(C=3\)

注意此时有一个很耍赖的情况,比如大家都是 \(1\),看起来先手胜,但其实后手可以等一步,然后乱杀先手。

但仔细研究发现如果是 \(1,1,2\) 这种情况,先手显然也可以留一手,就赢了。没有人想要自己的石子少一堆。

所以可以根据 \(SC\) 的奇偶性判断,如果是偶数先手胜,否则后手胜。

  • \(C=2\)

显然当只有两列的时候考虑 \(SC\) 奇偶性就行。

否则,少的一方一定获胜。

  • \(C=1\)

如果 \(A=2\)\(B=2\) 的话少的一方可以变成 \(C=2\)。一定获胜。

否则,如果 \(SA\ge SB+SC\),这时先手每一步,都可以减少 \(SB+SC\),这样,最后只会剩下 \(A\) 那一堆,必胜。\(SB\ge SA+SC\) 类似。

那剩下的答案只有可能与 \(SA+SB+SC\) 的奇偶性有关。

ABC173

E - Multiplication 4

又是贪心题。考场想歪浪费了好多时间。

先按abs排序取前k大然后看看是不是负数,如果是再考虑换一个数比较一下就行。

F - Intervals on Tree

又是结论题。

我们发现对于一条边它会把两个端点的连通块变成一个,所以先把树看成没有边的,每一条边减去有多少个区间包含其即可。

ABC185

D - Stamp

题意比较迷惑,看了样例才懂。

直接取每一段白色的长度的最小值,\(k\) 一定不能大于这个,而 \(k\) 越大越好,所以 \(k\) 一定等于这个。然后再对每一个空隙计算,设某一段是 \(L\),则需 \(\lceil\frac{L}{k}\rceil\)

E - Sequence Matching

思博dp题,记 \(dp[i][j]\) 为答案,有方程 \(dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1\),这是第一种转移,意义是删掉 \(i\) 或者 \(j\)

\(a[i]=b[j]\) 则还可以从 \(dp[i-1][j-1]\) 转移,因为有 \(a[i],b[j]\) 这一对不会影响答案。若没有则还可以 \(dp[i-1][j-1]\) 意义也是不删但是 \(y + 1\)

注意初始化 \(dp[i][0]=i,dp[0][j]=j,dp[0][0]=0\)

F - Range Xor Query

考虑第二种操作可以用前缀异或和做,\(sum(l,r)=sum(1,r)\bigoplus sum(1,l-1)\),所以我们可以维护前缀和。对于第一种操作可以看成单调加(异或满足交换律),所以可以用树状数组维护,基本就是树状数组板子改几个运算符。

ABC216

G - 01Sequence

以为是什么差分约束,结果发现有环 /kk

其实只需要贪心即可,按 r 排序,从小到大,枚举,每次不够了就往后填。用 bit 维护前缀和,set 维护数列。

H - Random Robots

一开始觉得神仙,但其实做多了发现也就那样。可以看成二维平面上的点,要不向右移动一格,要不向右上移动一格,最后会到达 \((N, X)\),并且路径不能有交。显然求方案数最后除以总方案数即可。考虑 LGV。但是发现数据范围不太能高斯消元。于是直接考虑行列式,发现答案就是 \(\sum (-1)^{w(p)}\prod \binom{N}{y_i-x_i}\),其中 \(y_i\) 是第 \(i\) 个点最后到了什么位置。注意这里 \(y_i\) 也必须有序(思考一下 lgv 在干啥?)

然后考虑对这个计数,发现不太能枚举排列,但是我们只关心逆序对数,考虑一个一个将数字加入排列 \(p\),只关心当前 \(p\) 中有哪些元素,所以可以状态。设 \(f[s,k]\) 代表当前加入元素为集合 \(s\)\(y_i\) 的最大值最多为 \(k\) 的权值和,这个最大值一定是在加入最后一个元素时得到的。这里把 \(-1\) 的贡献也考虑进去,转移就考虑会产生多少个逆序对。复杂度 \(O(k2^kn)\)。感觉有 \(O(poly(k)N)\) 的做法。最后答案就是 \(f[U][MAX]\)

ABC224

E - Integers on Grid

贪心考虑连到比其大且最小的数上,但由于这样的数可能很多,所以对每行列相同值的数建一个虚点,最后跑拓扑排序即可。

F - Problem where +s Separate Digits

考虑每个位置的贡献,容易发现答案即为 \(\sum_{i=1}^{n}s_i\sum_{j=0}^{n-i}10^{j}2^{\max(n-1-i-j,0)}\),后面那个提一个 \(2^{-i}\) 出来后可以预处理,于是就 \(O(n)\) 了。

G

H

ABC238

G - Cubic?

给定 \(N\) 和长为 \(N\) 的序列 \(A\)\(Q\) 组询问 \(l,r\),判断 \(\prod\limits_{i=l}^{r}A_i\) 是否是完全立方数。

\(1\le N, Q\le 2\times 10^5,1\le A_i\le 10^6\).

有什么哈希一类的不确定算法,但这里讲一种复杂度稍劣的确定性算法。

先根号分治,小于 \(1000\) 的质数直接暴力判断。然后大于 \(1000\) 的质数每个数最多有一个,于是莫队时候插入删除就都是 \(O(1)\) 了。

至于不确定算法,可以随机权值,然后做三进制不进位加法。

Ex - Removing People

\(n\) 个人排成一个圆,给定每个人的朝向。一共 \(n-1\) 次操作,每次选择一个人,然后将其看到的第一个人删掉并有代价即为两者距离。
这里距离的定义为以朝向为正方向,之间隔了几个人。并不是最短距离。求代价的期望值。
\(1\le n\le 300.\)

感觉最难的还是转换问题。考虑一个删除排列 \(P\)\(P_n\) 是最后剩下的那个。那么可以从后往前,也就是 \(P_n,P_{n-1}...\) 这样逐个确定每个值是在哪。而这时相当于确定之后有两个情况,在 \(P_{i+1},P_{i+2},...,P_{n}\) 里这些已有的选最靠近自己的那两个,看是否满足条件。

暴力的思维是 \(O(n!)\) 枚举,通常这样的暴力都可以用dp优化,因为有很多冗余的状态。显然对于已经确定的两个位置,之间的那些位置和这两个位置之外的部分就无关了。那么考虑区间dp。不过这是个环先得断环成链。之后记 \(f[i,j]\) 代表往开区间 \((i,j)\) 填的方案数。\(i,j\) 是已经填好了。转移考虑枚举一个 \(k\) 作为区间内第一个被选的位置。\(c_1\) 代表 \(k\) 有几种选择(选择 \(i\) 还是 \(j\))。有方程 \(f[i,j]=\sum\limits_{k=i+1}^{j-1}\binom{j-i-2}{k-i-1}f[i,k]f[k,j]c_1\)。这里组合数代表目前状态共有的 \(j-i-1\) 种选择去掉 \(k\) 再选 \(k-i-1\) 放到前面去。

然后记 \(g[i,j]\) 代表代价,最后答案即为 \(\dfrac{\sum\limits_{i=1}^{n}g[i,n+i]}{n!}\)。转移类似,不过这里需要计算 \(k\) 的贡献(不计算 \(i,j\) 互相的贡献)。需要记录一个 \(c_2\)。具体细节可以参考代码 45-48 这几行。感觉比写在这里清楚。

code

ABC242

Ex

我们只需知道每个集合包含集合中某一个元素的区间的数量,于是把它记到状态里,设 \(f_{i,j}\) 代表考虑到第 \(i\) 个数,第 \(i\) 个数必须选,有 \(j\) 个可选的区间的系数和(即带上 \(-1\))的系数。转移考虑枚举 \(k\) 代表上一个选的位置 \(f_{i,j+g_{k+1,i}}\gets -f_{k,j}\),其中 \(g_{l,r}\) 代表 \(L_k\ge l,L_k\le r, R_k\ge r\) 的数量。复杂度 \(O(n^2m)\)

ABC304

G - Max of Medians

Ex - Constrained Topological Sort

考虑从小到大贪心去填,这样子图的性质依据拓扑序就满足了,维护一个集合代表当前能选的数(即根据 \(L\)),显然是填在 \(R\) 尽量小的位置最优,于是拓扑排序去模拟这个过程即可。然后我们发现一个数的 \(R\) 并不一定有看起来那么大,比如说一条边 \(u,v\),就必须有 \(R_{u}<R_{v}\)。同理一个数的 \(L\) 也没有看上去那么小。注意这个 \(R\) 的更新得是有序的(拓扑序逆序)。加上这个更新就能通过了。

posted @ 2021-08-01 16:13  Semsue  阅读(86)  评论(0编辑  收藏  举报
Title