暴力枚举所有的a 3 a_{3} a 3 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <bits/stdc++.h> using ll = long long ;using namespace std;int main () { int t; cin >> t; while (t--) { int a1, a2, a4, a5; cin >> a1 >> a2 >> a4 >> a5; int res = 0 ; for (int a3 = -100 ; a3 <= 100 ; a3++) { int cnt = 0 ; cnt += a1 + a2 == a3; cnt += a2 + a3 == a4; cnt += a3 + a4 == a5; res = max (res, cnt); } cout << res << endl; } return 0 ; }
主要是读题。每行内部顺序无要求;如果第i i i 行的最小值小于第j j j 行的最小值,那么i i i 应该排在j j j 前面。而且如果按行按列都升序排序之后,必须是形如
0 3 6 1 4 7 2 5 8 \begin{array}{ccc} 0 & 3 & 6 \\ 1 & 4 & 7 \\ 2 & 5 & 8 \end{array} 0 1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <bits/stdc++.h> using ll = long long ;using namespace std;int main () { int t; cin >> t; while (t--) { int n, m; cin >> n >> m; vector a (n, vector<int >(m)) ; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { cin >> a[i][j]; } sort (a[i].begin (), a[i].end ()); } vector<int > p (n) ; iota (p.begin (), p.end (), 0 ); sort (p.begin (), p.end (), [&](int i, int j) { return a[i] < a[j]; }); bool ok = true ; for (int i = 1 ; i < n; i++) { for (int j = 0 ; j < m; j++) { if (a[p[i]][j] - 1 != a[p[i - 1 ]][j]) { ok = false ; } } } if (!ok) { cout << -1 << endl; } else { for (int i = 0 ; i < n; i++) { cout << p[i] + 1 << " " ; } cout << endl; } } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> using ll = long long ;using namespace std;int main () { int t; cin >> t; while (t--) { int n, k; cin >> n >> k; vector<int > cnt (n + 1 ) ; for (int i = 0 ; i < n; i++) { int x; cin >> x; cnt[x]++; } int ans = 0 ; for (int i = 1 ; i < (k + 1 ) / 2 ; i++) { ans += min (cnt[i], cnt[k - i]); } if (k % 2 == 0 ) { ans += cnt[k / 2 ] / 2 ; } cout << ans << endl; } return 0 ; }
一些分析:
对于前两个数,如果a 1 > a 2 a_{1}>a_{2} a 1 > a 2 那已经无解。
否则设a 1 ⩽ a 2 a_{1} \leqslant a_{2} a 1 ⩽ a 2 。需不需要操作前两项呢?
考虑a 1 < a 2 > a 3 a_{1}<a_{2}>a_{3} a 1 < a 2 > a 3 ,不能先操作a 2 a_{2} a 2 和a 3 a_{3} a 3 ,因为这样会让a 3 a_{3} a 3 变成 0,意味着只能a 1 = a 2 = 0 a_{1}=a_{2}=0 a 1 = a 2 = 0 ,但一般做不到。所以应当先操作a 1 a_{1} a 1 和a 2 a_{2} a 2 。
考虑a 1 < a 2 < a 3 > a 4 a_{1}<a_{2}<a_{3}>a_{4} a 1 < a 2 < a 3 > a 4 ,也不能先操作a 2 a_{2} a 2 和a 3 a_{3} a 3 ,因为这样会让a 2 a_{2} a 2 变成 0,a 1 > a 2 a_{1}>a_{2} a 1 > a 2 于是不可行。
归纳下来,最优方法就是从左往右顺着操作一遍,然后看是否升序。时间复杂度线性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <bits/stdc++.h> using ll = long long ;using namespace std;int main () { int t; cin >> t; while (t--) { int n; cin >> n; vector<int > a (n) ; for (int i = 0 ; i < n; i++) { cin >> a[i]; } for (int i = 1 ; i < n; i++) { int minn = min (a[i], a[i - 1 ]); a[i] -= minn; a[i - 1 ] -= minn; } cout << (is_sorted (a.begin (), a.end ()) ? "YES" : "NO" ) << endl; } return 0 ; }
如果原图有边,新图却不连通,那这条边必须删去。
现在只需要补边。用原图剩下的边建图,看有多少连通块,和新图差几个连通块就需要加几条边。
用并查集判断是否连通,时间复杂度近线性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include <bits/stdc++.h> using ll = long long ;using namespace std;struct DSU { vector<int > p, siz; DSU (int n) : p (n + 1 ), siz (n + 1 , 1 ) { iota (p.begin (), p.end (), 0 ); } int find (int x) { while (x != p[x]) x = p[x] = p[p[x]]; return x; } bool same (int x, int y) { return find (x) == find (y); } bool uno (int x, int y) { x = find (x), y = find (y); if (same (x, y)) return false ; siz[x] += siz[y]; p[y] = x; return true ; } int size (int x) { return siz[find (x)]; } }; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int t; cin >> t; while (t--) { int n, m1, m2; cin >> n >> m1 >> m2; vector<pair<int , int >> E1 (m1); for (auto & [u, v] : E1) { cin >> u >> v; u--, v--; } DSU dsu1 (n) , dsu2 (n) ; while (m2--) { int u, v; cin >> u >> v; u--, v--; dsu2. uno (u, v); } int res = 0 ; for (auto & [u, v] : E1) { if (!dsu2. same (u, v)) { res++; } else { dsu1. uno (u, v); } } int siz1 = 0 , siz2 = 0 ; for (int i = 0 ; i < n; i++) { siz1 += dsu1.f ind(i) == i; siz2 += dsu2.f ind(i) == i; } res -= siz2 - siz1; cout << res << endl; } return 0 ; }
首先如果n n n 很大,这些a i a_{i} a i 大部分都是 1,具体来说,2 20 > 1 0 5 2^{20}>10^{5} 2 2 0 > 1 0 5 ,所以至多只有 20 个非 1 的数,从这里入手。
设d p n , d dp_{n,d} d p n , d 表示a 1 a 2 … a d = n a_{1}a_{2}\dots a_{d}=n a 1 a 2 … a d = n 的方案数,其中a i ≠ 1 a_{i} \neq 1 a i = 1 ,于是可以做如下转移:
d p n , d ← d p x , d − 1 , x ∣ n , 1 < x < n dp_{n,d}\leftarrow dp_{x,d-1}, \quad x\mid n,\ 1<x<n d p n , d ← d p x , d − 1 , x ∣ n , 1 < x < n
表示最后一个填它某个因数,前面用已知的转移。方案数求和。
填 1。填上 1 之后总长度恰为ℓ \ell ℓ ,乘积为n n n 的方案数为
f ( n , ℓ ) = ∑ d = 1 ℓ C ℓ d d p n , d , f(n,\ell)=\displaystyle \sum_{d=1}^{\ell}\operatorname{C}_{\ell}^{d}dp_{n,d}, f ( n , ℓ ) = d = 1 ∑ ℓ C ℓ d d p n , d ,
也就是选d d d 个位置填不是 1 的数。
现在需要求最长长度是L L L 的,那就是
∑ ℓ = 1 L f ( n , ℓ ) = ∑ ℓ = 1 L ∑ d = 1 ℓ C ℓ d d p n , d . \displaystyle \sum_{\ell=1}^{L}f(n,\ell) =\sum_{\ell=1}^{L}\sum_{d=1}^{\ell}\operatorname{C}_{\ell}^{d}dp_{n,d}. ℓ = 1 ∑ L f ( n , ℓ ) = ℓ = 1 ∑ L d = 1 ∑ ℓ C ℓ d d p n , d .
这有两个∑ \sum ∑ ,复杂度不允许,利用组合数的性质化简,
= ∑ d = 1 L ∑ ℓ = d L C ℓ d d p n , d = ∑ d = 1 L ( ∑ ℓ = d L C ℓ d ) d p n , d = ∑ d = 1 L C L + 1 d + 1 d p n , d . =\sum_{d=1}^{L}\sum_{\ell=d}^{L}\operatorname{C}_{\ell}^{d}dp_{n,d} =\sum_{d=1}^{L}\Bigg(\sum_{\ell=d}^{L}\operatorname{C}_{\ell}^{d}\Bigg)dp_{n,d} =\sum_{d=1}^{L}\operatorname{C}_{L+1}^{d+1}dp_{n,d}. = d = 1 ∑ L ℓ = d ∑ L C ℓ d d p n , d = d = 1 ∑ L ( ℓ = d ∑ L C ℓ d ) d p n , d = d = 1 ∑ L C L + 1 d + 1 d p n , d .
这就是最终答案。
时间复杂度O ( D N N + t D N ) \operatorname{O}(DN\sqrt{ N}+tDN) O ( D N N + t D N ) ,这里D = 20 , N = 1 0 5 D=20,N=10^{5} D = 2 0 , N = 1 0 5 。本地能跑出来就没问题。可以用预处理因子的方法将复杂度降低到O ( D N log N + t D N ) \operatorname{O}(DN\log N+tDN) O ( D N log N + t D N ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #include <bits/stdc++.h> using ll = long long ;using namespace std;constexpr int mod = 998244353 ;constexpr int maxN = 1e5 + 8 ;constexpr int maxD = 20 ;ll dp[maxD][maxN]; ll qpow (ll a, ll n) { ll res = 1 ; while (n) { if (n & 1 ) res = res * a % mod; a = a * a % mod; n >>= 1 ; } return res; } ll inv (ll n) { return qpow (n, mod - 2 ); } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); for (int n = 1 ; n < maxN; n++) { dp[1 ][n] = 1 ; } for (int d = 2 ; d < maxD; d++) { for (int n = 1 ; n < maxN; n++) { for (int i = 2 ; i * i <= n; i++) { if (n % i == 0 ) { dp[d][n] += dp[d - 1 ][i]; dp[d][n] %= mod; if (n / i != i) { dp[d][n] += dp[d - 1 ][n / i]; dp[d][n] %= mod; } } } } } int t; cin >> t; while (t--) { int N, D; cin >> N >> D; cout << D << " " ; for (int n = 2 ; n <= N; n++) { ll res = 0 ; ll CC = (D + 1LL ) * D / 2 ; CC %= mod; for (int d = 1 ; d < min (maxD, D); d++) { res += CC * dp[d][n]; res %= mod; CC *= (D - d); CC %= mod; CC *= inv (d + 2 ); CC %= mod; } cout << res << " " ; } cout << endl; } return 0 ; }
附:证明
∑ k = n m C k n = C m + 1 n + 1 \displaystyle \sum_{k=n}^{m}\operatorname{C}_{k}^{n}=\operatorname{C}_{m+1}^{n+1} k = n ∑ m C k n = C m + 1 n + 1
LHS = C n n + C n + 1 n + C n + 2 n + ⋯ + C m n = ( C n + 1 n + 1 + C n + 1 n ) + C n + 2 n + ⋯ + C m n = C n + 2 n + 1 + C n + 2 n + ⋯ + C m n = ⋯ = C m n + 1 + C m n = RHS \begin{aligned} \operatorname{LHS} &=\operatorname{C}_{n}^{n}+\operatorname{C}_{n+1}^{n}+\operatorname{C}_{n+2}^{n}+\dots+\operatorname{C}_{m}^{n} \\ &=\big(\operatorname{C}_{n+1}^{n+1}+\operatorname{C}_{n+1}^{n}\big)+\operatorname{C}_{n+2}^{n}+\dots+\operatorname{C}_{m}^{n} \\ &=\operatorname{C}_{n+2}^{n+1}+\operatorname{C}_{n+2}^{n}+\dots+\operatorname{C}_{m}^{n} \\ &=\cdots \\ &=\operatorname{C}_{m}^{n+1}+\operatorname{C}_{m}^{n} \\ &=\operatorname{RHS} \end{aligned} L H S = C n n + C n + 1 n + C n + 2 n + ⋯ + C m n = ( C n + 1 n + 1 + C n + 1 n ) + C n + 2 n + ⋯ + C m n = C n + 2 n + 1 + C n + 2 n + ⋯ + C m n = ⋯ = C m n + 1 + C m n = R H S
高中还是蛮常用的。
题意 可执行任意次操作:
选定两个下标i i i 和j j j ,将a i a_i a i 与b j b_j b j 交换,同时将b i b_i b i 与a j a_j a j 交换。 问能否同时将a , b a,b a , b 按升序排序。
思路 有如下结论:
操作一次,a i a_{i} a i 对面还是b i b_{i} b i ,b i b_{i} b i 对面还是a i a_{i} a i ,但是上下关系会翻转(定义翻转为 swap( a i , b i ) (a_{i},b_{i}) ( a i , b i ) );
操作一次,有且仅有两组将翻转;
如果n ⩾ 3 n \geqslant 3 n ⩾ 3 ,则必然可以交换两列而不翻转(例如要换 1 列和 2 列,操作方案是 (1,3), (2,3), (1,2))。
所以现在化为,① 可以任意排序 ,② 只能翻转偶数次 (记翻转次数为 cnt),问是否有解。
再转换一下,不要关注中间是如何操作的 ,只要 ① 最终状态有序 ,② 初始状态和最终状态满足 翻转偶数次 ,就一定有操作方法,也就一定有解。
还是按 B 的思路排序:不妨翻转为a i < b i a_{i}<b_{i} a i < b i ,按a a a 升序排序,如果现在b b b 不是升序则无解。现在的问题是,在有序的前提下,能否 构造 出只翻转偶数次的序列。
先看两个例子:
比如序列[ 6 2 1 5 4 3 ] \begin{bmatrix}{\color{green}6} & 2 & 1 \\ {\color{green}5} & 4 & 3 \end{bmatrix} [ 6 5 2 4 1 3 ] 。直接排序得到[ 1 2 5 3 4 6 ] \begin{bmatrix}1 & 2 & {\color{green}5} \\ 3 & 4 & {\color{green}6} \end{bmatrix} [ 1 3 2 4 5 6 ] ,总共翻转奇数次,不行。而另一个有序序列是[ 1 2 6 3 4 5 ] \begin{bmatrix}1 & 2 & {\color{green}6} \\ 3 & 4 & {\color{green}5} \end{bmatrix} [ 1 3 2 4 6 5 ] ,总共翻转偶数次,可行。这就构造出了一个合法序列[ 1 2 6 3 4 5 ] \begin{bmatrix}1 & 2 & {\color{green}6} \\ 3 & 4 & {\color{green}5} \end{bmatrix} [ 1 3 2 4 6 5 ] 。
再如序列[ 7 6 2 1 5 8 4 3 ] \begin{bmatrix}{\color{green}7} & {\color{green}6} & 2 & 1 \\ {\color{green}5} & {\color{green}8} & 4 & 3 \end{bmatrix} [ 7 5 6 8 2 4 1 3 ] 。直接排序得到[ 1 2 5 6 3 4 7 8 ] \begin{bmatrix}1 & 2 & {\color{green}5} & {\color{green}6} \\ 3 & 4 & {\color{green}7} & {\color{green}8} \end{bmatrix} [ 1 3 2 4 5 7 6 8 ] ,总共翻转奇数次,不行。尝试将绿色部分整体翻转,得到[ 1 2 7 8 3 4 5 6 ] \begin{bmatrix}1 & 2 & {\color{green}7} & {\color{green}8} \\ 3 & 4 & {\color{green}5} & {\color{green}6} \end{bmatrix} [ 1 3 2 4 7 5 8 6 ] ,总共翻转奇数次,但还是不行。尝试将白色部分整体翻转,也不行。可以发现这种情况总是无解。
再次强调,不要关注中间是如何操作的,只要能构造出合法的最终状态就有解。
问题一:什么时候能整体翻转?
如果a i − 1 < b i , b i − 1 < a i a_{i-1}<b_{i},\ b_{i-1}<a_{i} a i − 1 < b i , b i − 1 < a i ,也即翻转后仍然有序,就可以把下标[ i , n ] [i,n] [ i , n ] 这一段整体翻转(也可以把下标[ 1 , i − 1 ] [1,i-1] [ 1 , i − 1 ] 这一段整体翻转)。 问题二:整体翻转能够构造出合法解吗?
如果某次翻转的段的长度是偶数,那翻转并不会改变 cnt 的奇偶性,没用;否则如果是奇数,就能随意改变 cnt 的奇偶性。 只要存在某个段为奇数,就能随意改变 cnt 的奇偶性,一定有解; 否则,cnt 的奇偶性在排序后就确定了,没有构造空间。 总结:在可能有序的前提下,如果能随意改变 cnt 的奇偶性,或者 cnt 本就是偶数,则有解。
时间复杂度O ( n log n ) \operatorname{O}(n\log n) O ( n log n ) ,主要是排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <bits/stdc++.h> using ll = long long ;using namespace std;int main () { int t; cin >> t; while (t--) { int n; cin >> n; vector<array<int , 2>> a (n); int cnt = 0 ; for (int i = 0 ; i < n; i++) { cin >> a[i][0 ]; } for (int i = 0 ; i < n; i++) { cin >> a[i][1 ]; if (a[i][0 ] > a[i][1 ]) { swap (a[i][0 ], a[i][1 ]); cnt++; } } sort (a.begin (), a.end ()); bool good = false ; bool ok = true ; for (int i = 1 ; i < n; i++) { if (a[i - 1 ][1 ] < a[i][0 ]) { if (i % 2 == 1 ) { good = true ; } } else if (a[i - 1 ][1 ] > a[i][1 ]) { ok = false ; } } if (ok && (good || cnt % 2 == 0 || n % 2 == 1 )) { cout << "Yes" << endl; } else { cout << "No" << endl; } } return 0 ; }