A,B 略。
C 建出一张图的邻接矩阵然后用 next_permutation
枚举排列判断是否能建立映射即可。
D 简单网格 dp,注意边界条件。
H × W H \times W H×W 的网格,你初始在 ( x 1 , y 1 ) (x_1, y_1) (x1,y1),要求做 K K K 次操作后位置变为 ( x 2 , y 2 ) (x_2, y_2) (x2,y2)。每次操作形如:
(就是车的移动方式)问有多少种可能的操作方案,两种操作方案不同当且仅当中间某一步经过的点不同。答案对 998244353 998244353 998244353 取模。
根据套路,这个东西的横向移动和纵向移动是可以分开来考虑的。即,我们可以考虑横向上走 m m m 步的方案,再乘上纵向上走 K − m K - m K−m 步的方案,再乘上组合系数 ( K m ) \dbinom K m (mK)。考虑计算在一个方向上走 m m m 步的方案数。
可以 DP,设 f 0 / 1 , i f_{0/1, i} f0/1,i 表示横向移动(纵向的类似)回到出发点与否,走 i i i 步的方案数,其中初始边界是 f 1 , 0 = 1 f_{1,0} = 1 f1,0=1,即走 0 0 0 步,终点在原点的方案数为 1 1 1。转移如下:
{ f 1 , i = ( H − 1 ) f 1 , i − 1 f 0 , i = f 1 , i − 1 + ( H − 2 ) f 0 , i − 1 \begin{cases} f_{1, i} = (H - 1)f_{1, i - 1}\\ f_{0, i} = f_{1, i - 1} + (H - 2)f_{0, i - 1} \end{cases} {f1,i=(H−1)f1,i−1f0,i=f1,i−1+(H−2)f0,i−1
转移的意义还是很显然的。于是我们 O ( n ) O(n) O(n) 求出 dp 数组,这题就做完了,评测记录。
给定两个长度为 N N N( 2 ≤ N ≤ 18 2\le N\le 18 2≤N≤18): A i A_i Ai 和 B i B_i Bi( 1 ≤ A i , B i ≤ 1 0 8 1\le A_i, B_i\le 10^8 1≤Ai,Bi≤108)。可以进行如下两种操作,问最小的使得 A A A 变成 B B B 的操作代价:
毫无疑问,两个操作是相互独立的,可以分开考虑。如果我们先交换,出一个 P 1 , P 2 , ⋯ , P N P_1, P_2,\cdots,P_N P1,P2,⋯,PN 的排列,然后再统计第一个的答案。令 inv ( P ) \operatorname{inv}(P) inv(P) 为 P P P 的逆序对数,则总代价为:
∑ i = 1 N ∣ A P i − B i ∣ ⋅ X + inv ( P ) ⋅ Y \sum_{i = 1}^N|A_{P_i} - B_i|\cdot X + \operatorname{inv}(P)\cdot Y i=1∑N∣APi−Bi∣⋅X+inv(P)⋅Y
把逆序对的这个关于 i i i 的贡献拆开,则我们可以得到
∑ i = 1 N ( ∣ A P i − B i ∣ ⋅ X + ∣ { p : p ∈ { 1 , 2 , ⋯ , N } \ { P 1 , ⋯ , P i − 1 } , p < P i } ∣ ⋅ Y ) \sum_{i = 1}^N(|A_{P_i} - B_i|\cdot X + |\{p:p\in\{1,2,\cdots, N\} \backslash \{P_1, \cdots, P_{i - 1}\}, p < P_i\}|\cdot Y) i=1∑N(∣APi−Bi∣⋅X+∣{p:p∈{1,2,⋯,N}\{P1,⋯,Pi−1},p<Pi}∣⋅Y)
然后会发现一个很关键的地方:后面那个东西只和 { P 1 , ⋯ , P i } \{P_1, \cdots, P_i\} {P1,⋯,Pi} 这个集合有关,而这个集合很小,所以不妨考虑将其状压起来,令 f ( x , S ) f(x, S) f(x,S) 表示 ∣ p : p ∈ { 1 , ⋯ , N } \ S , p < x ∣ |p:p\in\{1, \cdots, N\}\backslash S, p < x| ∣p:p∈{1,⋯,N}\S,p<x∣。然后式子改写为:
∑ i = 1 N ( ∣ A P i − B i ∣ ⋅ X + f ( P i , { P 1 , ⋯ , P i − 1 } ) ⋅ Y ) \sum_{i = 1}^N(|A_{P_i} - B_i|\cdot X + f(P_i, \{P_1, \cdots, P_{i - 1}\})\cdot Y) i=1∑N(∣APi−Bi∣⋅X+f(Pi,{P1,⋯,Pi−1})⋅Y)
于是发现,这个东西可以状压 dp,具体地,设 d p S dp_S dpS 表示排列的前 ∣ S ∣ |S| ∣S∣ 项是 S S S 内的元素,则转移应该是很好转移的。这题就做完了,评测记录。
给定一张 N N N 个点的有向完全图,其中, i i i 到 j j j 的有向边边权为 ( A i + B j ) m o d M (A_i + B_j)\bmod M (Ai+Bj)modM。问 1 1 1 到 N N N 的最短路。
2 ≤ N ≤ 2 × 1 0 5 2\le N\le 2\times 10^5 2≤N≤2×105, 2 ≤ M ≤ 1 0 9 2\le M\le 10^9 2≤M≤109。
首先如果我们直接连边跑 Dij,那么必然会寄,边数是 O ( n 2 ) O(n^2) O(n2) 级别的,要想办法少下来。
考虑一个技巧,建一张新图:
这样一来,从 i i i 走到 j j j 就相当于,从 i i i 走到 − A i m o d M ‾ \overline{-A_i\bmod M} −AimodM,然后一步步走到 B j ‾ \overline{B_j} Bj,再走到 j j j。发现中间的路程刚好就是 ( A i + B j ) m o d M (A_i + B_j)\bmod M (Ai+Bj)modM,于是问题就得到了转化,我们求新图上 1 1 1 到 N N N 的最短路即可。
可是, O ( n + m ) O(n + m) O(n+m) 似乎也必死无疑。
然而我们可以发现,环上的很多点是没有用的,我们可以将其缩起来,会和原来的点连接的虚点只有 2 n 2n 2n 个,这样子点数和边数就都控制在了 O ( n ) O(n) O(n) 级别,直接跑 Dij 便可通过,时间复杂度 O ( n log n ) O(n\log n) O(nlogn),评测记录。
不知道组题人怎么想的。。。这个题居然放在 H。
给定 H × W H\times W H×W 棋盘,一个国王初始在 ( 1 , 1 ) (1, 1) (1,1)。国王每步可以走到与其八连通的格子,请构造一个方案使得国王不重复地走完了每个格子,且终点为 ( a , b ) (a, b) (a,b)。 2 ≤ H , W ≤ 100 2\le H, W\le 100 2≤H,W≤100, ( a , b ) ≠ ( 1 , 1 ) (a, b)\neq (1, 1) (a,b)=(1,1)。
我们考虑减治构造。考虑如下几种情况:
如果 H = 2 H = 2 H=2,那么就可以像下面这样构造方案(图源 AtCoder 官方题解):
即,轨迹为: ( 1 , 1 ) → ( 2 , 1 ) → ⋯ → ( 1 , b ) → ( 1 , b + 1 ) → ⋯ → ( 1 , W ) → ( 2 , W ) → ⋯ → ( 2 , b ) (1, 1)\to (2, 1)\to\cdots\to(1, b)\to (1, b + 1)\to \cdots \to(1, W)\to (2, W)\to \cdots\to (2, b) (1,1)→(2,1)→⋯→(1,b)→(1,b+1)→⋯→(1,W)→(2,W)→⋯→(2,b)。如果 W = 2 W= 2 W=2,那么将行列调换之后是一样的。
考虑下面这个方案:
绿色圈出来的点集为 S S S,分类讨论:
然后就可以递归地去解决这道题了。这种代码写的很巧妙:
using pii = pair<int, int>;
vector<pii> solve(int h, int w, int a, int b) {
vector<pii> ret;
if (h == 2) {
FOR(i, 1, b - 1) ret.push_back(pii(1, i)), ret.push_back(pii(2, i));
ret.push_back(pii(3 - a, b));
FOR(i, b + 1, w) ret.push_back(pii(1, i));
DEC(i, w, b + 1) ret.push_back(pii(2, i));
ret.push_back(pii(a, b));
} else if ((h > 2 && w == 2) || b == 1 || (a == h && b == 2)) {
ret = solve(w, h, b, a);
for (auto &p : ret) myswap(p.first, p.second);
} else {
FOR(i, 1, h) ret.push_back(pii(i, 1));
auto res = solve(h, w - 1, h + 1 - a, b - 1);
for (auto &p : res) p.first = h + 1 - p.first, ++p.second;
ret.insert(ret.end(), res.begin(), res.end());
}
return ret;
}
int main() {
int h, w, a, b; read(h, w, a, b);
auto ans = solve(h, w, a, b);
for (auto p : ans) print(p.first, p.second);
return output(), 0;
}