[2021-09-04 AtCoder Beginner Contest 217] 题解
文章目錄
- A - Lexicographic Order
- B - AtCoder Quiz
- C - Inverse of Permutation
- D - Cutting Woods
- E - Sorting Queries
- F - Make Pair
- G - Groups
網址鏈接
A - Lexicographic Order
簽到題
#include <cstdio> #include <iostream> using namespace std; int main() {string s, t;cin >> s >> t;if( s < t ) printf( "Yes\n" );else printf( "No\n" );return 0; }B - AtCoder Quiz
簽到題
#include <cstdio> #include <iostream> using namespace std; char s1[10], s2[10], s3[10];int main() {scanf( "%s %s %s", s1, s2, s3 );if( 'R' != s1[1] and 'R' != s2[1] and 'R' != s3[1] )printf( "ARC\n" );if( 'B' != s1[1] and 'B' != s2[1] and 'B' != s3[1] )printf( "ABC\n" );if( 'H' != s1[1] and 'H' != s2[1] and 'H' != s3[1] )printf( "AHC\n" );if( 'G' != s1[1] and 'G' != s2[1] and 'G' != s3[1] )printf( "AGC\n" );return 0; }C - Inverse of Permutation
簽到題
#include <cstdio> #define maxn 200005 int n; int p[maxn], ans[maxn];int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )scanf( "%d", &p[i] ), ans[p[i]] = i;for( int i = 1;i <= n;i ++ )printf( "%d ", ans[i] );return 0; }D - Cutting Woods
簽到題
剛開始想歪了,打算不思考直接暴力線段樹cao
后來一看1e9的長度,打擾了
再一想,直接set不也可以做到線段樹的功能嗎
直接把操作點扔進去,找左右最近的分割點即可
#include <set> #include <cstdio> using namespace std; set < int > s; int n, Q, c, x;int main() {scanf( "%d %d", &n, &Q );s.insert( 0 ), s.insert( n );while( Q -- ) {scanf( "%d %d", &c, &x );if( c == 1 ) s.insert( x );else {auto l = s.lower_bound( x );auto r = l; l --;printf( "%d\n", *r - *l );}}return 0; }E - Sorting Queries
簽到題
可能會被排序操作嚇到TLE
實際上轉個彎就知道序列一定是前面有序后面無序的狀態
那么分開存進隊列即可,遇到排序就把無序的全都丟進有序隊列
每個點最多被操作入隊兩次
復雜度只有有序隊列的log罷了
這都是表面上嚇唬人的
#include <queue> #include <cstdio> using namespace std; priority_queue < int, vector < int >, greater < int > > q; queue < int > New; int Q, opt, x;int main() {scanf( "%d", &Q );while( Q -- ) {scanf( "%d", &opt );switch( opt ) {case 1 : {scanf( "%d", &x );New.push( x );break;}case 2 : {if( ! q.empty() )printf( "%d\n", q.top() ), q.pop();else printf( "%d\n", New.front() ), New.pop();break;}case 3 : {while( ! New.empty() )q.push( New.front() ), New.pop();break;}}} }F - Make Pair
中檔題
必須要兩人是好關系,并且相鄰才行
果斷區間dpdpdp,根據范圍200200200果斷猜測時間復雜度應為O(N3)/O(N3log?N)O(N^3)/O(N^3\log N)O(N3)/O(N3logN)
dpi,j:[i,j]dp_{i,j}:[i,j]dpi,j?:[i,j]區間的人都成功配對離開的方案數,答案自然為dp1,2ndp_{1,2n}dp1,2n?
考慮枚舉與iii配對的是kkk,則kkk將區間[i,j][i,j][i,j]隔絕成兩個互相獨立的子問題,dpi+1,k?1;;dpk+1,rdp_{i+1,k-1};;dp_{k+1,r}dpi+1,k?1?;;dpk+1,r?
定義dpi+1,i=1dp_{i+1,i}=1dpi+1,i?=1
假設[i+1,k?1][i+1,k-1][i+1,k?1]配對個數為x(k?1?(i+1)+1=k?i?1)x(k-1-(i+1)+1=k-i-1)x(k?1?(i+1)+1=k?i?1),[k+1,r][k+1,r][k+1,r]的配對個數為y(r?(k+1)+1=r?k)y(r-(k+1)+1=r-k)y(r?(k+1)+1=r?k)
dpi,j=∑k=i+1rdpi+1,k?1?dpk+1,r?(x+y+1y)dp_{i,j}=\sum_{k=i+1}^rdp_{i+1,k-1}*dp_{k+1,r}*\binom{x+y+1}{y}dpi,j?=k=i+1∑r?dpi+1,k?1??dpk+1,r??(yx+y+1?)
(x+y+1y)\binom{x+y+1}{y}(yx+y+1?):區間[i,j][i,j][i,j]的配對次數為x+y+1x+y+1x+y+1(i,ki,ki,k的一次配對),從中選擇yyy次操作右子區間
為什么不是從中選擇xxx次操作左子區間?
因為左子區間的操作事關i,ki,ki,k的相鄰問題,而右子區間無關
必須左子區間都操作完了才能操作(i,k)(i,k)(i,k)配對
所以說就不可能出現最后xxx次操作全都是操作左子區間,反而先操作(i,k)(i,k)(i,k)的配對,這樣是錯誤的,那么選xxx的組合數(x+y+1x)\binom{x+y+1}{x}(xx+y+1?)計算的方案數就是錯誤的
選擇右子區間操作的輪數后,剩下的x+1x+1x+1位置就固定了,最后一個一定是(i,k)(i,k)(i,k)的配對
#include <cstdio> #define int long long #define mod 998244353 #define maxn 405 int fac[maxn], inv[maxn]; bool good[maxn][maxn]; bool vis[maxn][maxn]; int dp[maxn][maxn]; int n, m;int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans; }void init() {fac[0] = inv[0] = 1;for( int i = 1;i < maxn;i ++ ) fac[i] = fac[i - 1] * i % mod;inv[maxn - 1] = qkpow( fac[maxn - 1], mod - 2 );for( int i = maxn - 2;i;i -- )inv[i] = inv[i + 1] * ( i + 1 ) % mod; }int C( int n, int m ) {return fac[n] * inv[m] % mod * inv[n - m] % mod; }int dfs( int l, int r ) {if( vis[l][r] ) return dp[l][r];if( l > r ) return vis[l][r] = dp[l][r] = 1;int &ans = dp[l][r];for( int i = l + 1;i <= r;i += 2 ) {if( ! good[l][i] ) continue;int x = i - l - 1 >> 1, y = r - i >> 1;ans = ( ans + dfs( l + 1, i - 1 ) * dfs( i + 1, r ) % mod * C( x + y + 1, y ) ) % mod; }vis[l][r] = 1;return ans; }signed main() {init();scanf( "%lld %lld", &n, &m );for( int i = 1, a, b;i <= m;i ++ ) {scanf( "%lld %lld", &a, &b );good[a][b] = good[b][a] = 1;}n <<= 1;printf( "%lld\n", dfs( 1, n ) );return 0; }G - Groups
簡單題
設dpi,j:dp_{i,j}:dpi,j?: 前iii個數一共使用了jjj個非空集合
那么轉移只會分為兩種
- iii單獨成一個新集合
- dpi,j+=dpi?1,j?1dp_{i,j}+=dp_{i-1,j-1}dpi,j?+=dpi?1,j?1?
- iii放進前jjj個集合中的某一個
- 集合無差別
- 每一個取模mmm相同的數都在不同的集合
- 前面與iii同余的個數顯然為?i?1m?\lfloor\frac{i-1}{m}\rfloor?mi?1??
- 不能放那些數所在的集合,剩下的集合都是可以放的
- dpi,j+=dpi?1,j?max?(0,j??i?1m?)dp_{i,j}+=dp_{i-1,j}*\max(0,j-\lfloor\frac{i-1}{m}\rfloor)dpi,j?+=dpi?1,j??max(0,j??mi?1??)
說實話,我覺得F G的題目分值給反了,前面竟然全是簽到水題,后面的經典DP還是可以給個好評的
總結
以上是生活随笔為你收集整理的[2021-09-04 AtCoder Beginner Contest 217] 题解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 贵州大数据:“云上贵州”昨天正式开通
- 下一篇: (在线接非法ddos)