AtCoder abc256全题解(区间合并模板、矩阵快速幂优化dp、线段树……)
文章目錄
- A
- B
- C-枚舉
- D-區(qū)間合并模板
- E-圖論建模,函數(shù)圖的性質(zhì)
- 題意
- 思路
- 代碼
- F-樹狀數(shù)組
- 題意
- 思路
- 代碼
- G-矩陣快速冪優(yōu)化dp
- H-線段樹
- 思路
- 實現(xiàn)
傳送門
本文CSDN
本文juejin
作者:hans774882968以及hans774882968
A
水,略。
B
模擬即可。
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> pii; #define rep(i,a,b) for(int i = (a);i <= (b);++i) #define re_(i,a,b) for(int i = (a);i < (b);++i) #define dwn(i,a,b) for(int i = (a);i >= (b);--i)const int N = 5 + 5;int n, a[N];void dbg() {puts (""); } template<typename T, typename... R>void dbg (const T &f, const R &... r) {cout << f << " ";dbg (r...); } template<typename Type>inline void read (Type &xx) {Type f = 1;char ch;xx = 0;for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';xx *= f; } void read() {} template<typename T, typename ...R>void read (T &x, R &...r) {read (x);read (r...); }int main() {read (n);int ans = 0;rep (_, 1, n) {int x;read (x);a[0]++;dwn (i, 3, 0) {if (i + x < 4) a[i + x] += a[i];else ans += a[i];a[i] = 0;}}printf ("%d\n", ans);return 0; }C-枚舉
題意:有一個九宮格,每格填一個正整數(shù),輸入3 <= h[0~2] <= 30, 3 <= w[0~2] <= 30分別表示期望的每行和每列的數(shù)字的和。求方案數(shù)。
只需要枚舉4個格子就能確定所有的數(shù)了,計算量30^4完全可行。我選擇的是(0,0), (0,1), (1,0), (1,1)這4個格子。
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> pii; #define rep(i,a,b) for(int i = (a);i <= (b);++i) #define re_(i,a,b) for(int i = (a);i < (b);++i) #define dwn(i,a,b) for(int i = (a);i >= (b);--i)const int N = 2e5 + 5;int n, w[5], h[5];void dbg() {puts (""); } template<typename T, typename... R>void dbg (const T &f, const R &... r) {cout << f << " ";dbg (r...); } template<typename Type>inline void read (Type &xx) {Type f = 1;char ch;xx = 0;for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';xx *= f; } void read() {} template<typename T, typename ...R>void read (T &x, R &...r) {read (x);read (r...); }int main() {n = 3;re_ (i, 0, n) read (w[i]);re_ (i, 0, n) read (h[i]);int ans = 0;re_ (i0, 1, min (w[0], h[0]) - 1) {re_ (i1, 1, min (w[1], h[0]) - 1) {re_ (i3, 1, min (w[0], h[1]) - 1) {re_ (i4, 1, min (w[1], h[1]) - 1) {int i2 = h[0] - i0 - i1, i5 = h[1] - i3 - i4, i6 = w[0] - i0 - i3, i7 = w[1] - i1 - i4;if (i2 > 0 && i5 > 0 && i6 > 0 && i7 > 0) {int i81 = h[2] - i6 - i7, i82 = w[2] - i2 - i5;if (i81 == i82 && i81 > 0) ++ans;}}}}}printf ("%d\n", ans);return 0; }D-區(qū)間合并模板
區(qū)間合并模板,參考:https://blog.nowcoder.net/n/834a656e47df44e58e830fdd87d3e253。
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> pii; #define rep(i,a,b) for(int i = (a);i <= (b);++i) #define re_(i,a,b) for(int i = (a);i < (b);++i) #define dwn(i,a,b) for(int i = (a);i >= (b);--i)const int N = 2e5 + 5;int n; pii a[N];void dbg() {puts (""); } template<typename T, typename... R>void dbg (const T &f, const R &... r) {cout << f << " ";dbg (r...); } template<typename Type>inline void read (Type &xx) {Type f = 1;char ch;xx = 0;for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';xx *= f; } void read() {} template<typename T, typename ...R>void read (T &x, R &...r) {read (x);read (r...); }int main() {read (n);rep (i, 1, n) read (a[i].first, a[i].second);sort (a + 1, a + n + 1);vector<pii> ans;for (int i = 1; i <= n; ++i) {int l = a[i].first, r = a[i].second;while (i <= n && a[i + 1].first <= r) r = max (r, a[++i].second);ans.push_back ({l, r});}for (pii v : ans) printf ("%d %d\n", v.first, v.second);return 0; }E-圖論建模,函數(shù)圖的性質(zhì)
題意
n <= 2e5個人排隊拿糖果。輸入長為n的兩個數(shù)組x, c,表示如果i號人排在x[i]號人后面,則i號人會受到c[i]的傷害。請你求一個排列,使所有人受到的總傷害最小。保證i ≠ x[i]。
思路
嘗試往逆序?qū)Α⒍帧⒇澬暮妥远x排序等方面想,一無所獲。只好看答案,沒想到是圖論題!
這個圖有n條有向邊,i -> x[i],官方題解指出這種圖叫做functional graph,顧名思義,以i為自變量,x[i]為因變量,確實符合函數(shù)定義。函數(shù)圖有如下特殊性質(zhì):它的每個無向圖意義下的連通分量恰好有一個環(huán)。官方題解用數(shù)學(xué)歸納法,用了不少篇幅來證明。其實感性上更好理解:每個點都必須有一個出度,而DAG拓撲序最大的點出度為0,故必定有環(huán)。而環(huán)套環(huán)要求有些點出度至少為2。
這樣每個連通分量就分為非環(huán)的部分和環(huán)上的部分。對于非環(huán)的部分,直接定順序為拓撲序則傷害為0,最優(yōu)。環(huán)上的部分至少有一個人會受傷害,那么我們只讓傷害值最小的人受傷(排最后)即可,貢獻min(c[y1],c[y2],...)。
代碼
F-樹狀數(shù)組
題意
給你長為n <= 2e5的數(shù)組。有兩種操作:
思路
套路題。首先考慮把它寫成只與a[1], a[2], ..., a[x]有關(guān)的形式。對于二階前綴和,用貢獻思想很容易發(fā)現(xiàn)是C[x] = x*a[1]+(x-1)*a[2]+...+1*a[x],所以D[x] = (x+2-1)*(x+1-1)/2*a[1]+(x+2-2)*(x+1-2)/2*a[1]+...+(x+2-x)*(x+1-x)/2*a[x]。
對于(x+2-i)*(x+1-i)/2*a[i],我們希望x的函數(shù)與和i有關(guān)的函數(shù)分離,則x的函數(shù)是系數(shù),和i有關(guān)的函數(shù)是需要維護的數(shù)組,這樣題目就做完了。分離出(x+2)*(x+1)/2 * a[i] - (2*x+3)/2 * (i*a[i]) + 1/2 * (i*i*a[i])。因此只需要維護a[i], i*a[i], i*i*a[i]的前綴和。
拓展:k階前綴和同理可做。但k較大的時候是否能寫代碼來求多項式系數(shù)呢?我暫時做不到。
代碼
- 注意一切皆可模法。
G-矩陣快速冪優(yōu)化dp
太難了,只能想到2^D的做法(枚舉頂點顏色的狀態(tài)位),直接學(xué)習(xí)題解了……
定義dp[i][S = 0~3]為填了前i條邊,第一個頂點和當(dāng)前的邊的頂點的顏色分別為狀態(tài)位的高位和低位的方案數(shù),1表示白色(1表示黑色同理可做)。顯然第n條邊的頂點和第一個頂點顏色要相同,所以答案dp[n][0] + dp[n][3]。
但我們發(fā)現(xiàn)沒法轉(zhuǎn)移,因為不知道之前的白色有幾種,沒法保證白色點數(shù)相同。那白色點數(shù)相同的保證就用外部限制來實現(xiàn),所以我們先枚舉所需顏色種類數(shù)k = 0~D+1。轉(zhuǎn)移:dp[i][S] = sum(C(D - 1, k - bc[S1]) * dp[i-1][S0]),這里S和S0的高位要相同,而S1 = (S % 2) << 1 | (S0 % 2)表示上一條邊的頂點和當(dāng)前邊的頂點顏色組成的狀態(tài)位,bc[]表示白色點的個數(shù)。邊界:dp[1][S] = C(D - 1, k - bc[S])。
轉(zhuǎn)移只與i-1有關(guān),可矩陣快速冪優(yōu)化。
總結(jié):想法要大膽,看到n較大也不一定就放棄思考dp做法了,因為還可以用矩陣快速冪來保證復(fù)雜度。
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> pii; #define rep(i,a,b) for(int i = (a);i <= (b);++i) #define re_(i,a,b) for(int i = (a);i < (b);++i) #define dwn(i,a,b) for(int i = (a);i >= (b);--i)const int N = 1e4 + 5; const int mod = 998244353;LL pw, fac[N], ifac[N]; int D, n = 4;void dbg() {puts (""); } template<typename T, typename... R>void dbg (const T &f, const R &... r) {cout << f << " ";dbg (r...); } template<typename Type>inline void read (Type &xx) {Type f = 1;char ch;xx = 0;for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';xx *= f; } void read() {} template<typename T, typename ...R>void read (T &x, R &...r) {read (x);read (r...); }struct Mat {LL m[5][5];Mat() {memset (m, 0, sizeof m);} }; Mat mul (Mat a, Mat b) {Mat ret;re_ (i, 0, n) {re_ (j, 0, n) {LL s = 0;re_ (k, 0, n) s = (s + a.m[i][k] * b.m[k][j] % mod) % mod;ret.m[i][j] = s;}}return ret; } Mat q_pow (Mat a, LL b) {Mat ret;re_ (i, 0, n) ret.m[i][i] = 1;for (; b; b >>= 1) {if (b & 1) ret = mul (ret, a);a = mul (a, a);}return ret; }LL q_pow (LL a, LL b) {LL ret = 1;for (; b; b >>= 1) {if (b & 1) ret = ret * a % mod;a = a * a % mod;}return ret; }void init_C (int n) {fac[0] = 1;rep (i, 1, n) fac[i] = fac[i - 1] * i % mod;ifac[n] = q_pow (fac[n], mod - 2);dwn (i, n - 1, 0) ifac[i] = (i + 1) * ifac[i + 1] % mod; }LL C (int x, int y) {if (y < 0 || x < y) return 0;return fac[x] * ifac[y] % mod * ifac[x - y] % mod; }int main() {init_C (N - 5);read (pw, D);LL ans = 0;rep (k, 0, D + 1) {Mat m0, bas;re_ (S, 0, n) {re_ (S0, 0, n) {if (S / 2 != S0 / 2) continue;int u = (S % 2) << 1 | (S0 % 2);m0.m[S][S0] = C (D - 1, k - __builtin_popcount (u) );}}re_ (S, 0, n) bas.m[S][0] = C (D - 1, k - __builtin_popcount (S) );Mat m1 = mul (q_pow (m0, pw - 1), bas);ans = (ans + m1.m[0][0] + m1.m[3][0]) % mod;}printf ("%lld\n", ans);return 0; }H-線段樹
思路
即ex題。官方題解的兩種做法都太難了,但是操作1的區(qū)間向下整除會導(dǎo)致數(shù)字快速減小,而操作2的區(qū)間覆蓋會導(dǎo)致整個數(shù)組的豐富程度下降,所以我們會想嘗試加一個tag,表示區(qū)間的數(shù)字是否完全相同,在區(qū)間數(shù)字完全相同時停止往下遍歷。
加這個tag的思路的主要來源是這道線段樹模板題:花神游歷各國,hdu上有一道題,區(qū)間求歐拉函數(shù)+求區(qū)間和,也是一樣的思路。
加這個tag的思路很容易理解,也能AC,但我不清楚它在這題的復(fù)雜度對不對。
實現(xiàn)
總結(jié)
以上是生活随笔為你收集整理的AtCoder abc256全题解(区间合并模板、矩阵快速幂优化dp、线段树……)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 知乎高赞!怎么自学 python,大概要
- 下一篇: 哔哩哔哩(B 站)刚刚崩了