題意:
給定一個括號序列,隨意交換兩個位置的括號之后,問有多少個不同長度的圈。關於圈的定義大概就是:將括號序列的后$k$個數放到括號序列的最前面,就是長度為$k$的圈。(看了好久題意emmm...)
分析:
首先,我們可以$n^2$暴力枚舉交換的位置,然后再看有多少個圈。
然后,對於括號序列的正確性判斷,有一個非常巧妙的方法,(只適用於只有一種括號,既有小括號,又有中括號是不得行的):
給$"("$賦值為1,$")"$賦值為-1,計算這個序列的前綴和,只要保證前綴和的每一位都大於等於0,並且最后一位剛好等於0,這個序列就是正確的括號序列。
然后再枚舉一個$k$,也就是圈的長度。要判斷這個$k$成不成立,也就是要保證變換之后的前綴和每一個都要大於等於0。
根據數學課上老師的傳授,這是一個恆成立問題,我們只需要讓最小的那個數在變換之后大於等於0就可以了。
用一個前綴最小值和后綴最小值,$m1[k]$表示$1~k$的最小值,$m2[k]$表示$k~n$的最小值,在變換之后,如果$m1[k]+(a[n]-a[i])>=0$&&$m2[k+1]-a[i]>=0$,那么就符合條件。

1 #include<iostream> 2 #include<string> 3 #include<cstdio> 4 #include<cstring> 5 #include<map> 6 #include<algorithm> 7 using namespace std; 8 #define N 505 9 #define ll long long 10 #define INF 0x3f3f3f3f 11 int n,ans,l,r; 12 char s[N]; 13 int a[N]/*前綴和*/,min1[N]/*前綴最小值*/,min2[N]/*后綴最小值*/; 14 void swp(int i,int j) 15 { 16 char tmp=s[i]; 17 s[i]=s[j],s[j]=tmp; 18 } 19 int f() 20 { 21 int res=0; 22 for(int i=1;i<=n;i++) 23 { 24 if(s[i]=='(') a[i]=a[i-1]+1; 25 if(s[i]==')') a[i]=a[i-1]-1; 26 min1[i]=min2[i]=INF; 27 min1[i]=min(min1[i-1],a[i]); 28 } 29 for(int i=n;i>=1;i--) 30 min2[i]=min(min2[i+1],a[i]); 31 if(min1[n]>=0&&a[n]==0) res++;//不進行輪換就已經是正確的序列 32 for(int i=1;i<n;i++)//從i和i+1之間斷開 33 if(min2[i+1]-a[i]>=0&&min1[i]+(a[n]-a[i])>=0&&a[n]==0) 34 res++; 35 return res; 36 //程序一開始的時候就判斷了a[n]!=0的情況 所以這里不寫其實也可以 37 } 38 int main() 39 { 40 scanf("%d",&n); 41 scanf("%s",s+1); 42 int cnt1=0,cnt2=0; 43 for(int i=1;i<=n;i++) 44 { 45 if(s[i]=='(') cnt1++; 46 else cnt2++; 47 } 48 if(cnt1!=cnt2) 49 { 50 puts("0\n1 1"); 51 return 0; 52 } 53 ans=f(),l=1,r=1; 54 for(int i=1;i<=n;i++) 55 for(int j=i+1;j<=n;j++) 56 { 57 if(s[i]==s[j]) continue; 58 swp(i,j); 59 int res=f(); 60 if(ans<res) ans=res,l=i,r=j; 61 swp(i,j); 62 } 63 printf("%d\n%d %d\n",ans,l,r); 64 return 0; 65 }