BZOJ5336: [TJOI2018]party
阿新 • • 發佈:2019-01-06
BZOJ5336: [TJOI2018]party
https://lydsy.com/JudgeOnline/problem.php?id=5336
分析:
- 好題。
- 正常的思路是設\(f[i][j][0/1/2]\)表示前\(i\)個位置,與獎章串的\(lcs\)狀態為\(j\),匹配到\(NOI\)的第幾位,然後轉移。
- 那麼問題是這個\(lcs\)的狀態如何儲存,打個表發現這個狀態數很少,實際上也是這樣的,因為在匹配\(lcs\)的過程中,相鄰兩位\(dp\)值最多差\(1\),狀態數\(2^k\)。
- 然後就做完了,預處理出來每個狀態能轉移到的狀態即可。
程式碼:
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> using namespace std; #define N 1050 #define mod 1000000007 char w[N]; int n,K; typedef long long ll; int tr[1<<15][3],sf[N],sg[N],cnt[1<<15]; ll f[2][1<<15][3],ans[N]; inline void upd(ll &x,ll y) { x=x+y; if(x>=mod) x-=mod; } int main() { scanf("%d%d%s",&n,&K,w+1); int i,mask=(1<<K)-1,s; for(s=0;s<=mask;s++) { for(i=1;i<=K;i++) { sf[i]=sf[i-1]+((s>>(i-1))&1); } for(i=1;i<=K;i++) { if(w[i]=='N') sg[i]=sf[i-1]+1; else sg[i]=max(sg[i-1],sf[i]); tr[s][0]|=(sg[i]-sg[i-1])*(1<<(i-1)); } for(i=1;i<=K;i++) { if(w[i]=='O') sg[i]=sf[i-1]+1; else sg[i]=max(sg[i-1],sf[i]); tr[s][1]|=(sg[i]-sg[i-1])*(1<<(i-1)); } for(i=1;i<=K;i++) { if(w[i]=='I') sg[i]=sf[i-1]+1; else sg[i]=max(sg[i-1],sf[i]); tr[s][2]|=(sg[i]-sg[i-1])*(1<<(i-1)); } } f[0][0][0]=1; for(i=0;i<n;i++) { int i0=i&1,i1=(i+1)&1; for(s=0;s<=mask;s++) { //O/I->N upd(f[i1][tr[s][0]][1],f[i0][s][0]); //O/I->O upd(f[i1][tr[s][1]][0],f[i0][s][0]); //O/I->I upd(f[i1][tr[s][2]][0],f[i0][s][0]); //N->N upd(f[i1][tr[s][0]][1],f[i0][s][1]); //N->O upd(f[i1][tr[s][1]][2],f[i0][s][1]); //N->I upd(f[i1][tr[s][2]][0],f[i0][s][1]); //NO->N upd(f[i1][tr[s][0]][1],f[i0][s][2]); //NO->O upd(f[i1][tr[s][1]][0],f[i0][s][2]); } memset(f[i0],0,sizeof(f[i0])); } for(i=0;i<=mask;i++) cnt[i]=cnt[i>>1]+(i&1); for(s=0;s<=mask;s++) for(i=0;i<3;i++) upd(ans[cnt[s]],f[n&1][s][i]); for(i=0;i<=K;i++) printf("%lld\n",ans[i]); }