LOJ#2452. 「POI2010」反對稱 Antisymmetry
阿新 • • 發佈:2019-01-25
不同的 lse lin name 可能 names 枚舉 奇數 esp 。
題目描述
對於一個 \(0/1\) 字符串,如果將這個字符串 \(0\) 和 \(1\) 取反後,再將整個串反過來和原串一樣,就稱作「反對稱」字符串。比如 \(00001111\) 和 \(010101\) 就是反對稱的,而 \(1001\) 就不是。
現在給出一個長度為 \(n\) 的 \(0/1\) 字符串,求它有多少個子串是反對稱的,註意這裏相同的子串出現在不同的位置會被重復計算。
輸入格式
第一行一個正整數 \(n\) 。
第二行一個長度為 \(n\) 的 \(0/1\) 字符串。
輸出格式
一行一個整數,表示原串的反對稱子串個數。
樣例
樣例輸入
8
11001011
樣例輸出
7
數據範圍與提示
對於 \(100\%\) 的數據, \(1\le n\le 500\ 000\)
Translated by vincent163
題解
馬拉車也是可以做的而且復雜度更優。
我還是菜啊沒有看出來單調性,這個取反翻轉操作是單調的,你一個大的串如果是反對稱串,中間的任何一個也肯定是。因為翻轉的位置並沒有變...
那麽枚舉起點,二分子串長度判斷即可。
因為取反後翻轉這個操作,所以不可能有奇數串符合條件(奇數串取反後翻轉了一定不相等),所以判斷也很好寫
然後比較坑的就是答案要用longlong存,我因為這個爆了好幾發,一開始還以為是進制數選的不好。
復雜度\(O(nlogn)\)
#include <bits/stdc++.h> using namespace std; typedef unsigned long long ull; typedef long long ll; const int N = 500010; const ull base = 13131; ull h1[N], h2[N], p[N]; int n; ll ans = 0; char s[N]; ull get_h1(int l, int r) { return h1[r] - h1[l - 1] * p[r - l + 1]; } ull get_h2(int l, int r) { return h2[l] - h2[r + 1] * p[r - l + 1]; } int check(int x) { int l = 1, r = min(x, n - x); while(l <= r) { int mid = (l + r) >> 1; if(get_h1(x - mid + 1, x) == get_h2(x + 1, x + mid)) l = mid + 1; else r = mid - 1; } return r; } int main() { scanf("%d%s", &n, s + 1); p[0] = 1; for(int i = 1; i <= n; ++i) p[i] = p[i - 1] * base, h1[i] = h1[i - 1] * base + (ull)s[i]; for(int i = n; i; --i) h2[i] = h2[i + 1] * base + (ull)(s[i] == '0' ? s[i] + 1 : s[i] - 1); for(int i = 1; i < n; ++i) ans += check(i); printf("%lld\n", ans); }
LOJ#2452. 「POI2010」反對稱 Antisymmetry