CF771E Bear and Rectangle Strips【貪心,dp】
阿新 • • 發佈:2021-06-26
給定 \(2\times n\) 的矩陣 \(t\),求最多能切分出多少個和為 \(0\) 的連續子矩陣。
\(n\le 3\cdot 10^5\),\(|t_{i,j}|\le 10^9\)。
樸素的想法是按列 dp,設 \(f_{i,j}\) 表示只考慮第一行前 \(i\) 格和第二行前 \(j\) 格時的答案,\(s_{0/1/2,i}\) 表示兩行/第一行/第二行的字首和,\(p_{0/1/2,i}\) 表示最大的 \(j\) 使得存在 \(j<k\le i\) 有 \(s_j=s_k\),則有 \(f_{i,j}=\max\{f_{i,p_{2,j}},f_{p_{1,i},j},f_{k,k}\}+1\)
然後就自閉了,正解是神必貪心:若 \(p_{1,i}\ge p_{2,j}\),則選了第二行 \((p_{2,j},j]\) 就一定要選第一行 \((p_{1,i},i]\),所以只用算 \(f_{p_{1,i},j}\),\(p_{1,i}<p_{2,j}\) 時同理。
結果記搜一下就過了,因為對於某個 \(i\),狀態只用到了 \(f_{i,i}\)、\(f_{i,j}=f_{i,i}+1\) 的某個 \(j\),\(f_{j,i}=f_{i,i}+1\) 的某個 \(j\),所以狀態數只有 \(O(n)\)
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 300003; template<typename T> void rd(T &x){ int ch = getchar(); bool f = false; x = 0; for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-'; for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0'; if(f) x = -x; } template<typename T> bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;} int n, p[3][N]; LL s[3][N]; unordered_map<LL, int> v[3]; unordered_map<int, int> f[N]; int calc(int x, int y){ if(x < 0 || y < 0) return -1e9; if(f[x].count(y)) return f[x][y]; int t = p[0][min(x,y)]; return f[x][y] = max(0, 1 + max(p[1][x]>p[2][y]?calc(p[1][x],y):calc(x,p[2][y]), calc(t,t))); } int main(){ rd(n); for(int i = 1;i <= 2;++ i) for(int j = 1;j <= n;++ j){ rd(s[i][j]); s[i][j] += s[i][j-1]; } memset(p, -1, sizeof p); v[0][0] = v[1][0] = v[2][0] = 0; for(int i = 1;i <= n;++ i){ s[0][i] = s[1][i] + s[2][i]; for(int j = 0;j < 3;++ j){ p[j][i] = p[j][i-1]; if(v[j].count(s[j][i])) chmax(p[j][i], v[j][s[j][i]]); v[j][s[j][i]] = i; } } f[0][0] = 0; printf("%d\n", calc(n, n)); }