Python 中的協程,到底是怎麼回事?
B. Eugene and an array-cf 1333C
題意:一串數字 可正可負 如果一串數字的和不為零且他的所有連續子段的和也不為零 那麼就稱之為good 求給定的一串數的good連續子串有多少個
思路
可以先預處理字首和 用pre記錄下來 如果有兩個字首和是一樣的 那麼除去他們重疊部分後一段的區間和就是0即不是good的
我們要求的是good的 那我們可以找出恰好不good的即區間和正好是0但子區間和都不是0的區間 然後將在這區間中以該區間右邊界為右邊界的所有good串貢獻給答案 不難發現這樣的數量是 r-l+1 個(若當前恰好good的區間是[l, r],就是除了等於自己的子區間為0 其他子區間都不可能為0)而我們只要記錄這一答案即可 因為如果l再往左移顯然包含了一個區間和為0的區間就不是good的
當然還有普遍的情況就是一個區間已確認是good的 那麼就直接貢獻區間長度(它的子段肯定是good的)
左右邊界都是任意的 便於計算和操作我們只要遍歷右邊界(即右邊界暫時固定)就能求全所有符合情況且不重複
對於找正好不是good的區間:
如果兩個字首和相等且相鄰那麼它們未重疊的部分就是正好不是good的區間
我們可以開兩個指標 l r以及一個mp記錄某個字首和的值是否出現過
遍歷r 將r經過的字首和都標記為1 即mp[pre[i]] = 1 當下次再遇到相同值的字首和那麼就知道前面已有了那麼兩個字首和的未重疊部分就是要找的恰好不good串 (因為r是從左到右遍歷裡的那就可以保證這兩個字首和是相鄰的)但是當前l r區間顯然是比我們所求區間大的那我們就要縮小範圍 將l右移 同時解除標記mp[pre[l]]因為當前l肯定是一個非good中的元素 不會再相應後面的相同的pre了否則得到非good串的就不是恰好非good的 直到解除某個字首l後當前的mp[pre[i]]不再是1 說明當前的[l, r]就是要找的恰好非good串
而在r遍歷的過程中如果當前pre[i]未被標記說明當前區間是good的那麼就直接加貢獻
#include<bits/stdc++.h> #include<unordered_map> #define ll long long #define ull unsigned long long #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; const ll inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-4; const ll N = 1e6 + 5; const int M = 1e5 + 5; const int mod = 1e9 + 7; ll n, a[N], pre[N]; map<ll, ll>mp; void solve() { cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; pre[i] = pre[i - 1] + a[i]; } ll l = 0, r = 1; ll ans = 0, flag = 0; for (int r = 1; r <= n; r++) { if (pre[r] == 0 && !flag && l == 0) {//當第一次出現字首和為0且前面沒有非good串時那這段是恰好good的就要貢獻區間長-1 即r-1 l++; flag = 1; } else while (mp[pre[r]]) {//已知道有一個非good串 那就去找 mp[pre[l]] = 0; l++; ans += r - l; continue; } mp[pre[r]] = 1; ans += r - l;因為每次找到了左區間又加1了所以直接r-l } cout << ans << "\n"; } signed main() { IOS; int t = 1; //cin >> t; while (t--) { solve(); } }