除了 n = 1 n=1 n = 1 时答案为 1 1 1 ,其余情况答案均为 2 2 2 .考虑值 v v v ,对于其左侧,只要有多于 2 2 2 个元素,就一定可以选三个(不选 v v v )然后删除一个;当左侧只剩下两个时,如果 v v v 大于另外两个,则可以删除最小值,否则可以删除最大值.
于是左右两侧都最多只有一个元素,此时可以同时选这三个数,并删去一个.于是最终答案为 2 2 2 .
Problem A 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 #include <bits/stdc++.h> void work () { int n; std::cin >> n; std::vector<int > a (n) ; for (auto &x : a) std::cin >> x; if (n <= 2 ) { for (int i = 0 ; i < n; i++) std::cout << n << " \n" [i == n - 1 ]; return ; } for (int i = 0 ; i < n; i++) std::cout << 2 << " \n" [i == n - 1 ]; } int main () { std::cin.tie (0 )->sync_with_stdio (false ), std::cout.tie (0 ); int t; std::cin >> t; while (t--) work (); }
首先当 x = y x=y x = y 时,我们肯定是先全部放 1 1 1 然后全部放 − 1 -1 − 1 ,这样就只有 1 1 1 种分隔方案.
接着不妨考虑 x > y x\gt y x > y .按上面的摆法,方案数为 ϕ ( x − y ) \phi(x-y) ϕ ( x − y ) ,即 ( x − y ) (x-y) ( x − y ) 的因子个数.这个也是最小的.
Problem B 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> void work () { int x, y; std::cin >> x >> y; int v = std::abs (x - y); int ans = 0 ; if (v != 0 ) { for (int i = 1 ; i * i <= v; i++) if (v % i == 0 ) ans += i * i == v ? 1 : 2 ; } else ans = 1 ; std::cout << ans << "\n" ; for (int i = 0 ; i < x; i++) std::cout << 1 << " " ; for (int i = 0 ; i < y; i++) std::cout << -1 << " " ; std::cout << "\n" ; } int main () { std::cin.tie (0 )->sync_with_stdio (false ), std::cout.tie (0 ); int t; std::cin >> t; while (t--) work (); }
考虑元素 v v v 在 a a a 数组里的下标为 p p p ,即 a p = v a_p=v a p = v .要想满足题目的要求,设 v v v 在 b [ ] b[] b [ ] 里的下标为 q q q ,那么对于 b [ ] b[] b [ ] 中每一个可以覆盖 p p p 的区间,v v v 都必须包含在其中.所以,q q q 应该是这样的区间的交集,即
q ∈ ⋂ l ∈ [ 1 , n − k + 1 ] p ∈ [ l , r ] [ l , r ] q\in \bigcap_{\scriptsize{\begin{aligned}l&\in [1,n-k+1]\\ p&\in [l,r]\end{aligned}}} [l,r]
q ∈ l p ∈ [ 1 , n − k + 1 ] ∈ [ l , r ] ⋂ [ l , r ]
令 L = n − k + 1 , R = k L=n-k+1,R=k L = n − k + 1 , R = k
如果 L ≤ R L\le R L ≤ R ,对于 p ∈ [ L , R ] p\in[L,R] p ∈ [ L , R ] 的元素 a p a_p a p ,他们就只能在这个 [ L , R ] [L,R] [ L , R ] 区间里重排.对于 [ L , R ] [L,R] [ L , R ] 之外的元素,则必须一一对应
如果 L > R L\gt R L > R ,说明不是所有的区间都重叠,这个情况下,p = q p=q p = q ,即 a , b a,b a , b 必须一一对应.
Problem C1 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 #include <bits/stdc++.h> void work () { int n, k; std::cin >> n >> k; std::vector<int > a (n + 1 ) , b (n + 1 ) ; for (int i = 1 ; i <= n; i++) std::cin >> a[i]; for (int i = 1 ; i <= n; i++) std::cin >> b[i]; int left = n - k + 1 , right = k; if (left <= right) { bool fixed = true ; for (int i = 1 ; i < left; i++) fixed &= b[i] == -1 or a[i] == b[i]; for (int i = right + 1 ; i <= n; i++) fixed &= b[i] == -1 or a[i] == b[i]; bool same = true ; std::set<int > f; for (int i = left; i <= right; i++) f.insert (a[i]); int cnt = 0 ; for (int i = left; i <= right; i++) { if (b[i] == -1 ) cnt++; else if (!f.count (b[i])) { fixed = false ; break ; } else { f.erase (b[i]); } } fixed &= cnt == f.size (); std::cout << (fixed ? "YES" : "NO" ) << "\n" ; } else { bool fixed = true ; for (int i = 1 ; i <= n; i++) { fixed &= b[i] == -1 or a[i] == b[i]; } std::cout << (fixed ? "YES" : "NO" ) << "\n" ; } } int main () { std::cin.tie (0 )->sync_with_stdio (false ), std::cout.tie (0 ); int t; std::cin >> t; while (t--) work (); }
我们用 W r a W_r^a W r a 表示区间 a [ r − k + 1 , r ] a[r-k+1,r] a [ r − k + 1 , r ] 对应的 multiset,因为 W r a = W r b , W r − 1 a = W r − 1 b W_r^a=W_r^b,W_{r-1}^a=W_{r-1}^b W r a = W r b , W r − 1 a = W r − 1 b ,所以有
W r a − W r − 1 a = W r b − W r − 1 b W_r^a-W_{r-1}^a=W_r^b-W_{r-1}^b
W r a − W r − 1 a = W r b − W r − 1 b
我们考虑把 multiset 看成是代表频率统计 的 n n n 维向量,用 e x ∈ N n e_x \in \mathbb{N}^n e x ∈ N n 表示 c n t [ x ] = 1 , c n t [ y ] = 0 , y ≠ x cnt[x]=1,cnt[y]=0,y\ne x c n t [ x ] = 1 , c n t [ y ] = 0 , y = x .那么 multiset 相减就可以看作向量相减,那么
e a r − e a r − k = e b r − e b r − k ⟹ e a r − e b r = e a r − k − e b r − k e_{a_{r}} - e_{a_{r-k}}=e_{b_r}-e_{b_{r-k}} \\
\implies e_{a_{r}} - e_{b_r}=e_{a_{r-k}}-e_{b_{r-k}}
e a r − e a r − k = e b r − e b r − k ⟹ e a r − e b r = e a r − k − e b r − k
于是我们可以按下标 mod k k k 进行分组处理.在每一组中可以分类讨论:
如果这一组中,a [ ] a[] a [ ] 全相等,那么 b [ ] b[] b [ ] 也必须全相等
如果已有的 b [ ] b[] b [ ] 有 ≥ 2 \ge 2 ≥ 2 个不同元素,直接不可能
否则,如果 b [ ] b[] b [ ] 有一个元素,那么所有 b [ ] b[] b [ ] 就只能是这个元素
否则 b [ ] b[] b [ ] 可以随便填
否则若 a [ ] a[] a [ ] 有不同元素,那么就必须满足 a i = b i a_i=b_i a i = b i ,这样所有 e a r − e b r = 0 ⃗ e_{a_r}-e_{b_r}=\vec{0} e a r − e b r = 0 .
第一种情况里还有额外需要考虑的:我们现在只考虑第一个窗口 [ 1 , k ] [1,k] [ 1 , k ] .在 “b [ ] b[] b [ ] 被强制填数” 这个情况下,b [ ] b[] b [ ] 填的数不受 a [ ] a[] a [ ] 控制,于是在第一个窗口里,有可能出现虽然要求的是 a i a_i a i 但是填的是 b i ≠ a i b_i\ne a_i b i = a i ,尽管通过了 e a i − e b i e_{a_i}-e_{b_i} e a i − e b i 全相等的判断,但却是错误的.所以,需要判断一下第一个窗口里填 b i b_i b i 的数量不会超过 a i a_i a i .
Problem C2 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 #include <bits/stdc++.h> void work () { int n, k; std::cin >> n >> k; std::vector<int > a (n + 1 ) , b (n + 1 ) ; std::vector<int > src (n + 1 , 0 ) , req (n + 1 , 0 ) ; for (int i = 1 ; i <= n; i++) std::cin >> a[i]; for (int i = 1 ; i <= n; i++) std::cin >> b[i]; bool ok = true ; int spare = 0 ; for (int r = 0 ; r < k; r++) { std::multiset<int > d; std::vector<int > idx; for (int i = 1 + r; i <= n; i += k) { d.insert (a[i]); idx.push_back (i); } if (*d.begin () != *d.rbegin ()) { for (auto i : idx) { if (b[i] != -1 and a[i] != b[i]) { std::cout << "NO\n" ; return ; } } } else { src[a[idx[0 ]]]++; std::multiset<int > db; for (auto i : idx) if (b[i] != -1 ) db.insert (b[i]); if (!db.empty () and *db.begin () != *db.rbegin ()) { std::cout << "NO\n" ; return ; } if (!db.empty ()) req[*db.begin ()]++; } } for (int i = 1 ; i <= n; i++) if (req[i] > src[i]) { std::cout << "NO\n" ; return ; } std::cout << "YES\n" ; } int main () { std::cin.tie (0 )->sync_with_stdio (false ), std::cout.tie (0 ); int t; std::cin >> t; while (t--) work (); }
思路很好想.看到位运算想拆位拆贡献 的做法.假设这 n n n 个数在第 p p p 位有 c p c_p c p 个数是 1 1 1 ,那么如果 subsequence 的长度为 k k k ,这一位的贡献为
( c p k ) 2 p \binom{c_p}{k}2^p
( k c p ) 2 p
所以答案便是 sum over p p p
b k = ∑ p = 0 29 2 p ( c p k ) b_k=\sum_{p=0}^{29} 2^p\binom{c_p}{k}
b k = p = 0 ∑ 2 9 2 p ( k c p )
然而这个 c p c_p c p 并不好计算.我们换一个思路:把 c p c_p c p 相同的 p p p 提出来,这样就可以 sum over c p c_p c p 从而消去这个不好枚举的东西:
b k = ∑ t = 0 n ( t k ) ( ∑ p : c p = t 2 p ) = ∑ t = 0 n ( t k ) m t \begin{aligned}
b_k&=\sum_{t=0}^n \binom{t}{k} \Big( \sum_{p:c_p=t}2^p \Big)\\
&=\sum_{t=0}^n \binom{t}{k} m_t
\end{aligned}
b k = t = 0 ∑ n ( k t ) ( p : c p = t ∑ 2 p ) = t = 0 ∑ n ( k t ) m t
所以,我们倒着枚举 k k k ,就可以一步一步解出 m n , m n − 1 , … , m 1 , m 0 m_n, m_{n-1},\dots, m_1, m_0 m n , m n − 1 , … , m 1 , m 0 .知道 { m t , t } \{m_t,t\} { m t , t } pair 后,就可以构造解了.
Problem D 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include <bits/stdc++.h> constexpr int Mod = 1e9 + 7 ;constexpr int N = 1e5 + 5 ;constexpr int B = 29 ;using i64 = long long ;struct mint { int v; mint () : v (0 ) {} mint (long long _v) : v (_v % Mod) {} static mint fpow (mint b, int p) { mint res = 1 ; while (p) { if (p & 1 ) res *= b; b *= b; p >>= 1 ; } return res; } static mint inv (mint s) { return fpow (s, Mod - 2 ); } mint operator +(mint r) { return v + r.v >= Mod ? v + r.v - Mod : v + r.v; } mint operator +=(mint r) { return *this = *this + r; } mint operator -(mint r) { return v < r.v ? v + Mod - r.v : v - r.v; } mint operator -=(mint r) { return *this = *this - r; } mint operator *(mint r) { return 1ll * v * r.v % Mod; } mint operator *=(mint r) { return *this = *this * r; } mint operator /(mint r) { return *this * inv (r); } mint operator /=(mint r) { return *this = *this / r; } friend std::istream &operator >>(std::istream &is, mint &c) { return is >> c.v; } friend std::ostream &operator <<(std::ostream &os, mint c) { return os << c.v; } }; std::vector<mint> fac (N, 1 ) , ifac (N, 1 ) ;mint C (int n, int k) { if (k < 0 || k > n) return 0 ; return fac[n] * ifac[k] * ifac[n - k]; } void work () { int n; std::cin >> n; std::vector<mint> v (n + 1 ) ; for (int i = 1 ; i <= n; i++) std::cin >> v[i]; std::vector<std::pair<int , mint>> have; std::vector<int > cnt (50 , 0 ) ; for (int k = n; k >= 1 ; k--) { mint sub{0 }; for (auto &[t, mt] : have) sub += mt * C (t, k); mint mk = v[k] - sub; if (mk.v != 0 ) { have.push_back ({k, mk}); for (int p = 0 ; p < B; p++) if (mk.v >> p & 1 ) cnt[p] = k; } } std::vector<int > a (n, 0 ) ; for (int p = 0 ; p < B; p++) { for (int i = 0 ; i < cnt[p]; i++) a[i] |= 1 << p; } for (auto x : a) std::cout << x << " " ; std::cout << "\n" ; } int main () { std::cin.tie (0 )->sync_with_stdio (false ), std::cout.tie (0 ); int t; std::cin >> t; for (int i = 1 ; i < N; i++) fac[i] = fac[i - 1 ] * i; ifac[N - 1 ] = mint::inv (fac[N - 1 ]); for (int i = N - 2 ; i >= 0 ; i--) ifac[i] = ifac[i + 1 ] * (i + 1 ); while (t--) work (); }
我们假设 d p [ i ] dp[i] d p [ i ] 表示覆盖 S ( i ) S(i) S ( i ) 的最少路径数,且其中恰好有一条经过 i i i .对于 good path,我们只需要知道其 gcd 即可.
此外,我们记录 l c m [ i ] lcm[i] l c m [ i ] 表示所有达到 d p [ i ] dp[i] d p [ i ] 的方案中,经过 i i i 的 good paths 的 gcd 值的 LCM
考虑怎么转移.对于读入的 a x , c h x [ ] a_x, ch_x[] a x , c h x [ ] ,我们首先判断能不能从某个子节点转移上来,即存在 j ∈ c h x [ ] j\in ch_x[] j ∈ c h x [ ] ,满足 gcd ( a x , l c m [ j ] ) > 1 \gcd(a_x, lcm[j])\gt 1 g cd( a x , l c m [ j ] ) > 1 .如果存在,那么
d p [ x ] ← ∑ j ∈ c h x [ ] d p [ j ] dp[x]\gets \sum_{j\in ch_x[]} dp[j]\\
d p [ x ] ← j ∈ c h x [ ] ∑ d p [ j ]
l c m [ x ] lcm[x] l c m [ x ] 需要记录所有可以达到 d p [ x ] dp[x] d p [ x ] 的方案里,good paths 的 gcd 值的 LCM.所以先求出所有的 l c m [ j ] lcm[j] l c m [ j ] 使得 gcd ( l c m [ j ] , a x ) > 1 \gcd(lcm[j], a_x)\gt 1 g cd( l c m [ j ] , a x ) > 1 ,再对这些 l c m [ j ] lcm[j] l c m [ j ] 求一个 LCM
l c m [ x ] ← lcm j : gcd ( l c m [ j ] , a x ) > 1 ( l c m [ j ] ) lcm[x]\gets \mathop{\texttt{lcm}}\limits_{j:\gcd(lcm[j],a_x)\gt 1}(lcm[j])
l c m [ x ] ← j : g c d ( l c m [ j ] , a x ) > 1 lcm ( l c m [ j ] )
否则,如果不能从子节点转移上来,那么 x x x 自己就必须是单独一条 good path,于是
d p [ x ] ← 1 + ∑ j ∈ c h x [ ] d p [ j ] l c m [ x ] ← a x dp[x]\gets 1+ \sum_{j\in ch_x[]} dp[j]\\
lcm[x]\gets a_x
d p [ x ] ← 1 + j ∈ c h x [ ] ∑ d p [ j ] l c m [ x ] ← a x
Problem E 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 #include <bits/stdc++.h> using i64 = long long ;void work () { int n; std::cin >> n; std::vector<i64> dp (n + 1 , 0 ) , lcm (n + 1 , 0 ) , ans (n + 1 , 0 ) ; for (int i = n; i >= 1 ; i--) { i64 a; int k; std::cin >> a >> k; i64 sum = 0 , curlcm = 1 ; for (int t = 0 , c; t < k; t++) { std::cin >> c; sum += dp[c]; i64 g = std::gcd (a, lcm[c]); if (g > 1 ) curlcm = std::lcm (curlcm, g); } if (curlcm == 1 ) { dp[i] = 1 + sum; lcm[i] = a; } else { dp[i] = sum; lcm[i] = curlcm; } ans[i] = dp[i]; std::cout << ans[i] << std::endl; } } int main () { std::cin.tie (0 )->sync_with_stdio (false ), std::cout.tie (0 ); int t; std::cin >> t; while (t--) work (); }