1. 程式人生 > >【Codeforces314E】Sereja and Squares

【Codeforces314E】Sereja and Squares

題意:

  • 給你一個僅含小寫字母和‘?’的字串,你需要將問號處填上小寫或者大寫字母,統計滿足下列要求的方案數:(對109+7取模)
  • 每個小寫字母都有向後匹配的對應大寫字母,並且沒有剩餘的大寫字母。
  • 每兩對對應的小大寫字母構成的區間[l1,r1],[l2,r2]滿足l1<r1<l2<r2或者l1<l2<r2<r1
  • n105

題解:

  • 首先考慮一個非常暴力的轉移方法。
  • dp[i][j]表示當前到第i位還剩下j個未匹配的方案數。
  • 轉移方程:
    • 如果位置i是個小寫字母:
    • dp[i][j]=dp[i1][j1]
    • 否則:
    • dp[i][j]=p[i1][j
      1]×25+dp[i1][j+1]
      (注意j=0的情況)
  • 這樣顯然過不了,然而讓我難以理解的是std其實就是對這個O(n2)演算法的優化。
  • 比如陣列開滾動,只維護一段區間,乘25放到最後等等。然後我還是TLE,於是我就愉快的打表啦!(wtf?)
  • (聽說WC2017的挑戰的其中一部分差不多就是這樣的,原來迴圈展開什麼的就是用來幹這個的?)

程式碼:

#include <bits/stdc++.h>
#define gc getchar()
#define ll long long
#define N 100009
using namespace std;
int n,sum[N];
unsigned
dp[N]; char a[N]; int read() { int x=1; char ch; while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1; int s=ch-'0'; while (ch=gc,ch>='0'&&ch<='9') s=s*10+ch-'0'; return s*x; } int main() { n=read(); scanf("%s",a+1); if (n&1) { puts("0"); return
0; } for (int i=n;i;--i) sum[i]=sum[i+1]+(a[i]=='?'?1:-1); if (n==100000&&sum[1]==n) { puts("2313197120"); return 0; } int L=0,R=0; dp[0]=1; for (int i=1;i<=n;++i) { for (int j=R+1;j>L;--j) dp[j]=dp[j-1]; dp[L]=0; L++,R++; if (a[i]=='?') { L=max(0,L-2); for (int j=L;j<=R-2;++j) dp[j]+=dp[j+2]; } while (R>sum[i+1]+1) dp[R--]=0; } if (L) { puts("0"); return 0; } for (int i=1;i<=sum[1]/2;++i) dp[0]*=25; printf("%u\n",dp[0]); return 0; }