1. 程式人生 > >LOJ#2452. 「POI2010」反對稱 Antisymmetry

LOJ#2452. 「POI2010」反對稱 Antisymmetry

不同的 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