Educational Codeforces Round 119 (Rated for Div. 2) C. BA-String
阿新 • • 發佈:2022-01-06
Educational Codeforces Round 119 (Rated for Div. 2) C題BA-String題解
題目
- 原題地址:C. BA-String
- 題目編號:1620C
- 目標演算法:dp(動態規劃)
- 難度評分:1800
1.題目大意
- 給你一串序列長為 n,每位為 a 或 * ,n ≥ 1
- 每個 * 可以換成 0 到 k 個 b
- 重複的序列算一個,即連續的 * 中不同 * 替換成 b 後可能相同
- 求:按照字典序排列的第 x 個字串
2.題目分析
- 既然可以通過改變 b 的數量和位置來實現按照字典序進行排序,那麼這之間必然存在著一種對應關係。
- 我的想法是,先開一個數組,把一段連續的 * 進行統計,a 進行單獨標記為 -2, 最後用 -1 標記統計結束。
- 例如:a * * * a * * a * * * * a
- 轉換成陣列後就變成了: -2, 3, -2, 2, -2, 4, -2, -1
- 通過舉例分析,將按照字典序排好的序列的順序實際上可以通過每位不同進位制來實現。
- 還是上面的例子,先不看 a ,則每段連續的 * 最多可以替換成 b 的個數為:
- 3k, 2k, 4k
- 由於替換成的 b 可以從 0 開始取,所以每位的進位制(最多可以表示的不同的數)為:
- 3k+1, 2k+1, 4k+1
- 需要注意的是,由於採用進位制的方式是從 0 開始計數,所以 x 一開始要減 1。
- 所以到這裡本題就變成了將 x-1 轉換成一個特定的變進位制數,每位的數值就是每段連續的 * 需要替換成的 b 的個數。
- 繼續按照上面的例子來,具體的轉換方式:
x = x - 1;
B3 = x % (4k + 1);
x /= (4k + 1);
B2 = x % (2k + 1);
x /= (2k + 1);
B1 = x % (3k + 1);
- (如果看不懂為什麼這麼做可以自己代數試試找找規律)
- 由於本題還需要在每組 b 之中插入 a ,所以直接在上面的陣列中進行操作,同時將轉換得到的進位制數覆蓋原來統計的個數即可(倒序進行),最後再順序遍歷陣列輸出就行。
- 本題的幾個坑:
- k 可以為 0
- 輸入序列中可以沒有 *
- a 可以連續出現
3.題目程式碼
#include <iostream> using namespace std; int main() { int t; //freopen("./in.txt","r",stdin); cin >> t; while(t--) { int n, k; long long x; cin >> n >> k >> x; x--;//按照進位制來說是從0開始計數,因此要減一 long long cnt[n+5] = {0}; char ch; int i=0, j=0; cin >> ch;//首位特判 if(ch=='*')//是*就統計個數 cnt[j]++; else//是a就設定為-2 { cnt[j] = -2; j++; } for(i=1,j;i<n;i++) { cin >> ch; if(ch=='*')//是*就統計個數 cnt[j]++; else//是a就設定為-2 { if(cnt[j]!=0) j++; cnt[j] = -2; j++; } } if(cnt[j]==0)//最後一個是a { cnt[j] = -1;//表示結束 j--; } else//最後一個是* cnt[j+1] = -1; while(cnt[j]==-2&&j>=0)//輸入最後一位為a,就倒退,直至到達*,也有可能沒有* j--; for(j;j>=0;j--) { if(cnt[j]!=-2) { //cout << cnt[j] << " "; long long tmp = cnt[j] * k + 1 ;//加一轉化為對應進位制數 if(k==0) { cnt[j] = 0; continue; } cnt[j] = x % (tmp); x /= tmp; } } for(int m=0;cnt[m]!=-1;m++) { if(cnt[m]==-2) cout << 'a'; else for(int z=0;z<cnt[m];z++) cout << 'b'; } cout << endl; } return 0; } //1 //61 4 99 //aaaa*aa*aaa**aa*a****aaaa******aaaaa*a***a*aaaaa*a**aaa*aaaa*