Codeforces 990C (模擬+組合數學)

題面:

傳送門

分析:

此題O(n2l) 模擬肯定是會超時的(l爲所有字符串總長)
我們想到對字符串進行一定的預處理,可以快速計算匹配
我們設每一個(的值爲1,)的值爲-1,規定
若只有)括號多了x個,則l[i]=r[i]=-x<0
若只有(括號多了x個,則l[i]=r[i]=x>0
那麼如何求l[i],r[i]的值呢?
從左到右掃描字符串,用一個變量cnt,統計和
cnt的最小值=l[i],最終的值=r[i]
若(和)都有多餘,則必須接兩個字符串,不符合條件,所以計算時應該直接跳過這些字符串,不用考慮

要理解這句話,請看下面的模擬過程
)()
字符串下標(0開始) 0 1 2
cnt值 -1 0 -1
l[i]=-1,說明左邊需要一個(括號來匹配 (>=0即不需要)
r[i]=-1,說明右邊不需要(<=0即不需要),而左邊需要一個(括號

我們以all[i]來hash,哈希表的第 x位存儲了所有r[i]=x的字符串的l[i]
但要注意的是,由於負數的原因,數組下標要人爲加上一個數addv來存儲

爲了防止一對字符串被算兩次,我們規定字符串只能接在右邊,我們枚舉字符串時要先保證l[i]>=0才能接,而l[i]<0的只能接在其他字符串右邊
對於一個l[i]>=0的字符串i,我們尋找接在它右側能匹配的字符串,所以我們在哈希表的r[i] 位置尋找l[i]值爲r[i] 的字符串

比如:()(的l[i]=0,r[i]=1,我們則要尋找r[i]=l[i]=-1,即只多了一個)括號的字符串

用二分查找統計值爲r[i] 的字符串的個數,答案+=值爲r[i] 的字符串的個數
預處理時間複雜度O(l) (l爲所有字符串總長)
二分查找時間複雜度O(nlog2l)

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector> 
#include<algorithm>
#define addv 300005
#define maxn 2*addv
using namespace std;
int n;
char str[maxn];
int cnt;
int l[maxn];
int r[maxn];
vector<int>table[maxn];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",str);
        int len=strlen(str);
        l[i]=maxn;
        cnt=0;
        for(int j=0;j<len;j++){//像分析中一樣求l[i],r[i]
            if(str[j]=='(') cnt++;
            else cnt--;
            l[i]=min(l[i],cnt);
        }
        r[i]=cnt;
        table[r[i]+addv].push_back(l[i]);//hash,爲了防止負數人爲加上addv,addv就相當於新的零點
    }
    for(int i=1;i<maxn;i++) if(table[i].size()!=0) sort(table[i].begin(),table[i].end());//排序,爲了二分查找
    long long ans=0;
    for(int i=1;i<=n;i++){
        if(l[i]>=0){
            int tmp=addv-r[i];//即-r[i]
            ans+=table[tmp].end()-lower_bound(table[tmp].begin(),table[tmp].end(),-r[i]);//lower_bound返回第一個>=x的位置,用結尾去-它,可求出l[j]=r[j]=-r[i]的j的個數
        }
    }
    printf("%I64d\n",ans);
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章