Atcoder AGC031B Reversi (DP计数)
Atcoder AGC031B Reversi (DP計數)
簡單的計數題。(總算做出一道AGC的B題了,然而這場比賽我忘記打了233333)
題目鏈接: https://atcoder.jp/contests/agc031/tasks/agc031_b
題意: 有一個長度為\(N\)的顏色序列,第\(i\)個位置初始顏色為\(a_i\), 可以執行若干次操作,每次可以選擇兩個顏色一樣的位置,然后把這兩個位置中間的區間都刷成和兩端相同的顏色,問最后本質不同的序列有多少種。
題解: 最重要的想法就是要深刻地理解本質不同。
因為我們不論如何操作,最后得到的序列一樣就算一樣,所以假設\([l_1,r_1]\)和\([l_2,r_2]\)分別是先后兩次操作。
若前者和后者相交但不包含,若前后兩次刷成的顏色相同,我們可以等效成一次操作,操作區間為它們的并。(等效法?文化課走火入魔了吧)若前后兩次顏色不同,那么這種情況一定是不存在的,因為相交但不包含意味著第一個區間的一個端點在第二個區間里,那這個端點在執行完前面的操作之后就不再是顏色2而變成顏色1了。
若前者包含后者,則后者無用。
若后者包含前者,則前者無用。
若兩次區間不相交,則兩次都有用。
所以本題就是要求把長度為\(N\)的序列內取出若干不相交區間,每個區間兩端點顏色相同的方案數。
我們先對序列進行如下處理: 把所有連續的顏色相同的區間縮成一個位置。例如122333441變成12341.
然后\(f_i\)表示前\(i\)個的方案數。
\(f_i=\sum_{j\le i, a_j=a_i} f_{j-1}\)
桶優化。
時間復雜度\(O(n)\).
特發此文,假裝自己還沒AFO。
代碼
好長啊,500多B.
#include<cstdio> #include<cstdlib> #include<cstring> #define llong long long using namespace std;const int N = 2e5; const int P = 1e9+7; int a[N+3],b[N+3]; llong f[N+3],g[N+3]; int n;int main() {scanf("%d",&n);for(int i=1; i<=n; i++) scanf("%d",&a[i]);int m = 0; for(int i=1; i<=n; i++) if(i==0 || a[i]!=a[i-1]) {m++; b[m] = a[i];}n = m; for(int i=1; i<=n; i++) a[i] = b[i];f[0] = 1ll; g[a[1]] = 1ll;for(int i=1; i<=n; i++){f[i] = g[a[i]];g[a[i+1]] = (g[a[i+1]]+f[i])%P;}printf("%lld\n",f[n]);return 0; } 發表于 2019-03-17 00:01 suncongbo 閱讀(...) 評論(...) 編輯 收藏 刷新評論刷新頁面返回頂部總結
以上是生活随笔為你收集整理的Atcoder AGC031B Reversi (DP计数)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Codeforces 1106F Lun
- 下一篇: Atcoder AGC031C Diff