日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

AtCoder abc256全题解(区间合并模板、矩阵快速幂优化dp、线段树……)

發布時間:2024/1/8 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AtCoder abc256全题解(区间合并模板、矩阵快速幂优化dp、线段树……) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

      • A
      • B
      • C-枚舉
      • D-區間合并模板
      • E-圖論建模,函數圖的性質
        • 題意
        • 思路
        • 代碼
      • F-樹狀數組
        • 題意
        • 思路
        • 代碼
      • G-矩陣快速冪優化dp
      • H-線段樹
        • 思路
        • 實現

傳送門

本文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-枚舉

題意:有一個九宮格,每格填一個正整數,輸入3 <= h[0~2] <= 30, 3 <= w[0~2] <= 30分別表示期望的每行和每列的數字的和。求方案數。

只需要枚舉4個格子就能確定所有的數了,計算量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-區間合并模板

區間合并模板,參考: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-圖論建模,函數圖的性質

題意

n <= 2e5個人排隊拿糖果。輸入長為n的兩個數組x, c,表示如果i號人排在x[i]號人后面,則i號人會受到c[i]的傷害。請你求一個排列,使所有人受到的總傷害最小。保證i ≠ x[i]。

思路

嘗試往逆序對、二分、貪心和自定義排序等方面想,一無所獲。只好看答案,沒想到是圖論題!

這個圖有n條有向邊,i -> x[i],官方題解指出這種圖叫做functional graph,顧名思義,以i為自變量,x[i]為因變量,確實符合函數定義。函數圖有如下特殊性質:它的每個無向圖意義下的連通分量恰好有一個環。官方題解用數學歸納法,用了不少篇幅來證明。其實感性上更好理解:每個點都必須有一個出度,而DAG拓撲序最大的點出度為0,故必定有環。而環套環要求有些點出度至少為2。

這樣每個連通分量就分為非環的部分和環上的部分。對于非環的部分,直接定順序為拓撲序則傷害為0,最優。環上的部分至少有一個人會受傷害,那么我們只讓傷害值最小的人受傷(排最后)即可,貢獻min(c[y1],c[y2],...)。

代碼

  • uf[]標記無向圖意義下的連通分量是否被訪問過。簡單的并查集。
  • 為了得到環上的所有點:vis[]是中間變量,u從i開始,跳到第一次vis[u] = true的時候,u是環上的一點,接著跳就能得到環上的所有點了。
  • #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, c[N], a[N], fa[N]; bool vis[N], uf[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 find (int x) {return x == fa[x] ? x : fa[x] = find (fa[x]); }int main() {read (n);re_ (i, 0, n) read (c[i]), c[i]--;re_ (i, 0, n) read (a[i]);re_ (i, 0, n) fa[i] = i;re_ (i, 0, n) {int f1 = find (i), f2 = find (c[i]);fa[f1] = f2;}LL ans = 0;re_ (i, 0, n) {int fi = find (i);if (uf[fi]) continue;uf[fi] = true;vis[i] = true;int u = c[i];while (!vis[u]) {vis[u] = true;u = c[u];}int mn = a[u];for (int v = c[u]; v != u; v = c[v])mn = min (mn, a[v]);ans += mn;}printf ("%lld\n", ans);return 0; }

    F-樹狀數組

    題意

    給你長為n <= 2e5的數組。有兩種操作:

  • 單點修改。
  • 求3階前綴和D[x](見原題,1-indexed),模998244353。
  • 思路

    套路題。首先考慮把它寫成只與a[1], a[2], ..., a[x]有關的形式。對于二階前綴和,用貢獻思想很容易發現是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的函數與和i有關的函數分離,則x的函數是系數,和i有關的函數是需要維護的數組,這樣題目就做完了。分離出(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較大的時候是否能寫代碼來求多項式系數呢?我暫時做不到。

    代碼

    • 注意一切皆可模法。
    #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) #define lowbit(x) ((x) & (-(x)))const int N = 2e5 + 5; const int mod = 998244353; const int I2 = 499122177;int n, q, a[N]; LL c0[N], c1[N], c2[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...); }void mdy (LL C[], int x, LL v) {for (; x <= n; x += lowbit (x) ) C[x] = (C[x] + v) % mod; }LL qry (LL C[], int x) {LL ret = 0;for (; x; x -= lowbit (x) ) ret = (ret + C[x]) % mod;return ret; }int main() {read (n, q);rep (i, 1, n) read (a[i]), a[i] %= mod;rep (i, 1, n) {mdy (c0, i, a[i]);mdy (c1, i, 1LL * i * a[i] % mod);mdy (c2, i, 1LL * i * i % mod * a[i] % mod);}while (q--) {int op;read (op);if (op == 1) {int x;LL v;read (x, v);v %= mod;mdy (c0, x, (v - a[x] + mod) % mod);mdy (c1, x, x * (v - a[x] + mod) % mod);mdy (c2, x, 1LL * x * x % mod * (v - a[x] + mod) % mod);a[x] = v;} else {LL x;read (x);LL k0 = (x + 2) * (x + 1) % mod * I2 % mod, k1 = (2 * x + 3) * I2 % mod;LL ans = (k0 * qry (c0, x) % mod - k1 * qry (c1, x) % mod + I2 * qry (c2, x) % mod + mod) % mod;printf ("%lld\n", ans);}}return 0; }

    G-矩陣快速冪優化dp

    太難了,只能想到2^D的做法(枚舉頂點顏色的狀態位),直接學習題解了……

    定義dp[i][S = 0~3]為填了前i條邊,第一個頂點和當前的邊的頂點的顏色分別為狀態位的高位和低位的方案數,1表示白色(1表示黑色同理可做)。顯然第n條邊的頂點和第一個頂點顏色要相同,所以答案dp[n][0] + dp[n][3]。

    但我們發現沒法轉移,因為不知道之前的白色有幾種,沒法保證白色點數相同。那白色點數相同的保證就用外部限制來實現,所以我們先枚舉所需顏色種類數k = 0~D+1。轉移:dp[i][S] = sum(C(D - 1, k - bc[S1]) * dp[i-1][S0]),這里S和S0的高位要相同,而S1 = (S % 2) << 1 | (S0 % 2)表示上一條邊的頂點和當前邊的頂點顏色組成的狀態位,bc[]表示白色點的個數。邊界:dp[1][S] = C(D - 1, k - bc[S])。

    轉移只與i-1有關,可矩陣快速冪優化。

    總結:想法要大膽,看到n較大也不一定就放棄思考dp做法了,因為還可以用矩陣快速冪來保證復雜度。

    #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的區間向下整除會導致數字快速減小,而操作2的區間覆蓋會導致整個數組的豐富程度下降,所以我們會想嘗試加一個tag,表示區間的數字是否完全相同,在區間數字完全相同時停止往下遍歷。

    加這個tag的思路的主要來源是這道線段樹模板題:花神游歷各國,hdu上有一道題,區間求歐拉函數+求區間和,也是一樣的思路。

    加這個tag的思路很容易理解,也能AC,但我不清楚它在這題的復雜度對不對。

    實現

  • 結構體需要3個屬性:v,ctg,s。v = -1表示該區間的值不是完全相同,v >= 0表示值完全相同;ctg表示區間覆蓋的懶標記;s表示區間和。
  • 我選擇了先build,再對數組進行n次單點覆蓋,這就要求把葉節點的值設為0,而非葉節點的ctg設為-1。也可以直接讀入數組再build。
  • s不是可選的而是必須的,把s去掉則查區間和的時候會走得更深,導致TLE。
  • #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) #define ls(x) ((x) << 1) #define rs(x) ((x) << 1 | 1)const int N = 5e5 + 5;int n, q;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 Node {int v, ctg;LL s; } nd[N << 2];void update (int x, int v, int l, int r) {nd[x].ctg = nd[x].v = v;nd[x].s = 1LL * v * (r - l + 1); }void pushup (int x) {nd[x].v = nd[ls (x)].v == nd[rs (x)].v ? nd[ls (x)].v : -1;nd[x].s = nd[ls (x)].s + nd[rs (x)].s; }void pushdown (int x, int l, int r) {if (!~nd[x].ctg) return;int v = nd[x].ctg, mid = (l + r) >> 1;nd[ls (x)].ctg = nd[rs (x)].ctg = v;nd[ls (x)].v = nd[rs (x)].v = v;nd[ls (x)].s = (mid - l + 1LL) * v;nd[rs (x)].s = 1LL * (r - mid) * v;nd[x].ctg = -1; }void build (int x, int l, int r) {nd[x].ctg = -1;if (l == r) {update (x, 0, l, r);return;}int mid = (l + r) >> 1;build (ls (x), l, mid);build (rs (x), mid + 1, r);pushup (x); }void idiv (int ql, int qr, int v, int x = 1, int l = 1, int r = n) {if (ql <= l && r <= qr && ~nd[x].v) {update (x, nd[x].v / v, l, r);return;}pushdown (x, l, r);int mid = (l + r) >> 1;if (ql <= mid) idiv (ql, qr, v, ls (x), l, mid);if (qr > mid) idiv (ql, qr, v, rs (x), mid + 1, r);pushup (x); }void cover (int ql, int qr, int v, int x = 1, int l = 1, int r = n) {if (ql <= l && r <= qr && ~nd[x].v) {update (x, v, l, r);return;}pushdown (x, l, r);int mid = (l + r) >> 1;if (ql <= mid) cover (ql, qr, v, ls (x), l, mid);if (qr > mid) cover (ql, qr, v, rs (x), mid + 1, r);pushup (x); }LL qry (int ql, int qr, int x = 1, int l = 1, int r = n) {if (ql <= l && r <= qr) return nd[x].s;pushdown (x, l, r);int mid = (l + r) >> 1;LL ans = 0;if (ql <= mid) ans += qry (ql, qr, ls (x), l, mid);if (qr > mid) ans += qry (ql, qr, rs (x), mid + 1, r);pushup (x);return ans; }int main() {read (n, q);build (1, 1, n);rep (i, 1, n) {int v;read (v);cover (i, i, v);}while (q--) {int op, l, r, v;read (op, l, r);if (op == 1) {read (v);idiv (l, r, v);} else if (op == 2) {read (v);cover (l, r, v);} else {printf ("%lld\n", qry (l, r) );}}return 0; }

    總結

    以上是生活随笔為你收集整理的AtCoder abc256全题解(区间合并模板、矩阵快速幂优化dp、线段树……)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。