前言:

因为要普及了,今年没一等就可以退役去学文化课了,所以暑假把历年noip普及组都刷了一遍,离noip还有50+天,想弄点强化训练什么的。

想了想,就这些天学文化课之余有空就把AtCoder之前那些ARC 的 C D E 什么的刷一下吧(一般是D,可能会有简单一点的E和难一点的C)(可能会很慢,毕竟基本有时间也就周末了)

所以就开了这个坑鞭策一下自己,上个坑是dp的,开了50题,补的巨累...这次吸取教训,只开20题...(也没那么多时间去刷题了)

不会说题意,题意的话可以上网找或者去原站看,只会简述做法以及附上代码


目前20/20


1.[AtCoder ARC059D/ABC043D]Unbalanced

题目链接

总结:结论题

一找就是一道神题...

知道结论后觉得挺简单挺显然的,但是不知道结论的时候真的死活想不出来

对于一个不平衡的串,显然它至少有一个子串是这个样子的“$XX$” ,"$XYX$",$X$和$Y$分别代表不同的字母

然后这样的子串也是最简的不平衡的串。

所以只要在原串中$O(n)$扫一遍,找出形如$XX$,$XYX$的串就可以了(有SPJ)(但是我并不会证明为什么在一个不平衡的串里面一定会有形如$XX$,$XYX$的子串,如果有哪位大大会证可以在评论区里说一下吗QAQ)

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. char s[ ] ;
  6.  
  7. int main() {
  8. scanf( "%s" , s + ) ;
  9. int n = strlen( s + ) ;
  10. for( int i = ; i <= n ; i ++ ) {
  11. if( s[ i ] == s[ i + ] ) {
  12. printf( "%d %d\n" , i , i + ) ;
  13. return ;
  14. }
  15. if( s[ i ] == s[ i + ] ) {
  16. printf( "%d %d\n" , i , i + ) ;
  17. return ;
  18. }
  19. }
  20. puts( "-1 -1" ) ;
  21. return ;
  22. }

arc059D


2.[AtCoder ARC059 E]Children and Candies

题目链接

总结:dp+前缀和优化

好像不是特别难的样子...不过一大堆sigma我一开始看着有点萌币

其实就是当你这个人拿了x个糖果之后呢,前面的方案也就要相应的乘上$\sum_{i=a[i]}^{b[i]} i^m$,所以这个转移是$O(n^4)$的

不过因为$ai,bi$都是固定的所以直接前缀和优化一下就行了

数据也就$400$,$O(n^3)$能过了

但是注意转移时的取模,我之前没搞好$wa$了好几个点...

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 500
  6. #define ll long long
  7. const ll mod = 1e9+ ;
  8.  
  9. ll n , m ;
  10. ll a[ N ] , b[ N ] ;
  11. ll sum[ N ][ N ] , l[ N ][ N ] ;
  12. ll f[ N ][ N ] ;
  13. //f[ i ][ j ]前i个人一共分了j个糖果
  14. //l[ i ][ j ]储存i的j次方
  15. //sum[ i ][ j ]对l维护前缀和
  16.  
  17. int main() {
  18. scanf( "%lld%lld" , &n ,&m ) ;
  19. for( int i = ; i <= n ; i ++ ) {
  20. scanf( "%lld" , &a[ i ] ) ;
  21. }
  22. for( int i = ; i <= n ; i ++ ) {
  23. scanf( "%lld" , &b[ i ] ) ;
  24. }
  25. for( int i = ; i < N ; i ++ ) {
  26. l[ i ][ ] = ;
  27. for( int j = ; j < N ; j ++ ) {
  28. l[ i ][ j ] = l[ i ][ j - ] * i ;
  29. l[ i ][ j ] %= mod ;
  30. }
  31. }
  32. for( int i = ; i < N ; i ++ ){
  33. for( int j = ; j < N ; j ++ ) {
  34. sum[ i ][ j ] += sum[ i - ][ j ] + l[ i ][ j ] ;
  35. sum[ i ][ j ] %= mod ;
  36. }
  37. }
  38. f[ ][ ] = ;
  39. for( int i = ; i <= n ; i ++ ) {
  40. for( int j = ; j <= m ; j ++ ) {
  41. for( int k = ; k <= j ; k ++ ) {
  42. f[ i ][ j ] = ( f[ i ][ j ] % mod + ( 1ll * f[ i - ][ j - k ] *( ( sum[ b[ i ] ][ k ] - sum[ a[ i ] - ][ k ] ) % mod ) + mod ) % mod + mod ) % mod ;
  43. }
  44. }
  45. }
  46. printf( "%lld\n" , f[ n ][ m ] ) ;
  47. return ;
  48. }

arc059E


3.[AtCoder ARC059F]Unhappy Hacking

题目链接

总结:dp+乘法逆元

这个F题是假的吧...感觉没有F题难度

就设$f[i][j]$表示按了$i$次键盘,然后一共出现了$j$个字母的情况

刚开始愣是想不到怎么转移,因为可以$f[i][j]$可以从好几个状态转移过来...

秒了一眼题解发现可以用$f[i][j]$去更新别人...所以就转移两次就行了

$f[i+1][j+1]+=2*f[i][j]$(因为可以按0或者1,所以要乘2)

$f[i+1][min(j-1,0)]+=f[i][j]$(这里是按后退键的情况,因为0也是合法状态所以要算进去,取个$min$)

但是这样子算出来的$f[n][len]$是能够用$n$次操作拼出长度为$len$的字符串的方法总数(一共可以拼出$2^{len}$个串),所以要除一下$2^{len}$

但是因为有取模所以要用一下乘法逆元,这里用快速幂来求乘法逆元

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 5010
  6. #define ll long long
  7. const int mod = 1e9+ ;
  8.  
  9. int n ;
  10. char s[ N ] ;
  11. int f[ N ][ N ] ;
  12. //f[ i ][ j ] 表示按了i次键盘,出现了j个字母
  13.  
  14. ll power( ll a , ll b ) {
  15. ll ans = , base = a ;
  16. while( b ) {
  17. if( b& ) ans = ( ans * base ) % mod ;
  18. base = ( base * base ) % mod ;
  19. b >>= ;
  20. }
  21. return ans % mod ;
  22. }
  23.  
  24. int main() {
  25. scanf( "%d%s" , &n , s+ ) ;
  26. int len = strlen( s + ) ;
  27. f[ ][ ] = ;
  28. for( int i = ; i <= n ; i ++ ) {
  29. for( int j = ; j <= i ; j ++ ) {
  30. f[ i + ][ j + ] += * f[ i ][ j ] ;
  31. f[ i + ][ j + ] %= mod ;
  32. f[ i + ][ max( j - , ) ] += f[ i ][ j ] ;
  33. f[ i + ][ max( j - , ) ] %= mod ;
  34. }
  35. }
  36. ll ans =1ll*power( power( , len ) , mod - ) % mod * f[ n ][ len ] ;
  37. ans %= mod ;
  38. printf( "%lld" , ans ) ;
  39. return ;
  40. }

arc059F


4.[AtCoder ARC060C]Tak and Cards

题目链接

总结:dp

一开始只能想到爆搜的做法...

我们可以设$f[i][j][k]$表示前$i$个人中选了$j$个人,他们的平均数为$k$的最优解

那么初始化$f[0][0][0]=1$

转移方程:

$f[i][j][k]+=f[i-1][j][k]$(不取)

$f[i][j][k]+=f[i-1][j-1][k-a[i]]$(取)

就类似于背包那样子去搞

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. int n , ar , a[ ] ;
  6. long long f[ ][ ][ ] ;
  7. //选到i,选了j个,sum为k
  8.  
  9. int main() {
  10. scanf( "%d%d" , &n , &ar ) ;
  11. for( int i = ; i <= n ; i ++ ) {
  12. scanf( "%d" , &a[ i ] ) ;
  13. }
  14.  
  15. f[ ][ ][ ] = ;
  16.  
  17. for( int i = ; i <= n ; i ++ ) {
  18. for( int j = i ; j >= ; j -- ) {
  19. for( int k = ar * n + ; k >= ; k -- ) {
  20. f[ i ][ j ][ k ] += f[ i - ][ j ][ k ] ;
  21. if( k >= a[ i ] && j ) f[ i ][ j ][ k ] += f[ i - ][ j - ][ k - a[ i ] ] ;
  22. }
  23. }
  24. }
  25. long long ans = ;
  26. for( int i = ; i <= n ; i ++ ) {
  27. ans += f[ n ][ i ][ ar * i ] ;
  28. }
  29. printf( "%lld\n" , ans );
  30. }

ARC060C


5.[AtCoder ARC060D]Digit Sum

题目链接

总结:数学题

被数学题虐爆...研究了好久的题解才明白这题...

题意:给一个函数$f(b,n)$可以求出$n$在$b$进制下各位数的和,设$f(b,n)=s$,现在已知$n,s$,求$b$

我们可以分成两种情况来讨论

当$b<=\sqrt{n}$时,我们可以直接枚举$b$,然后套用题目的公式来计算

当$b>\sqrt{n}$时,我们可以寻找一下规律:

设$n$在$b$进制下的高位为$p$低位为$q$(因为$b>\sqrt{n}$,所以这里的$n$在$b$进制下一定是两位数)

这时的$n=pb+q$ , $s=p+q$

所以$n-s=(b-1)p$

即$$b=\frac{n-s}{p}+1$$

因为$b$是整数,所以枚举$n-s$的所有因数$p$就可以了,不过枚举之后要更新答案的时候记得再套一遍$f(b,n)$来检查一下,因为算出来的$b$可能会出现某些奇奇怪怪的东西(亲测...)

复杂度是$O(\sqrt{n})$的

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define inf 0x3f3f3f3f
  6. #define ll long long
  7.  
  8. ll n , s ;
  9.  
  10. ll f( ll b , ll n ) {
  11. return n < b ? n : f( b , n / b ) + n % b ;
  12. }
  13.  
  14. int main() {
  15. scanf( "%lld%lld" , &n , &s ) ;
  16. if( s > n ) return puts( "-1" ) , ;
  17. if( s == n ) return printf( "%lld\n" , n + ) , ;
  18. ll m = sqrt( n ) + ;
  19. for( ll i = ; i <= m ; i ++ ) {
  20. if( f( i , n ) == s )
  21. return printf( "%lld" , i ) , ;
  22. }
  23. ll ans = 1e11 ; n -= s ;
  24. for( ll i = ; i * i <= n ; i ++ ) {
  25. if( n % i == ) {
  26. ll b = n / i + ;
  27. if( f ( b , n + s ) == s ) ans = min ( ans , b ) ;
  28. }
  29. }
  30. printf( "%lld\n" , ans != 1e11 ? ans : - ) ;
  31. return ;
  32. }

ARC060D


6.[AtCoder ARC060E]Tak and Hotels

题目链接

总结:分块+二分

首先用二分求出每个点每一天能够走到哪个点(贪心地想,能走更远肯定走更远,至于如果走过了,肯定在那天的一半的时候就走到目的地了,所以时间花费还是一样的)

然后我一开始是只处理出这个然后就一个个去跳了...结果只$A$了第一个点,其他全部$TLE$

所以要考虑优化,想了好久忽然想起弹飞绵羊那题,这题除了没有修改操作其实是和弹飞绵羊差不多的

所以大力分块!

二分预处理不变,然后对原序列分块。再维护一下每个点跳出该块后在哪以及跳出该块要花多少天

在查询的时候就可以一个块一个块的跳跳到目的地所在的那一块,剩下的路径暴力就可以,这样复杂度最坏是$O(2\sqrt{n})$的

复杂度是$O(nlogn+q\sqrt{n})$的,AtCoder的时限三秒,很松,能过

我写完后上网看了一下别人的做法怎么清一色的倍增...

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 100010
  6.  
  7. int n , L , m ;
  8. int a[ N ] , to[ N ] ;
  9. int num , block , belong[ N ] ;
  10. int nxt[ N ] , val[ N ] ;
  11.  
  12. int find( int x ) {
  13. int l = x + , r = n , ans = ;
  14. while( l <= r ) {
  15. int mid = ( l + r ) >> ;
  16. if( a[ mid ] - a[ x ] <= L ) l = mid + , ans = mid ;
  17. else r = mid - ;
  18. }
  19. return ans ;
  20. }
  21.  
  22. int main() {
  23. scanf( "%d" , &n ) ;
  24. for( int i = ; i <= n ; i ++ ) {
  25. scanf( "%d" , &a[ i ] ) ;
  26. }
  27. sort( a + , a + n + ) ;
  28. scanf( "%d%d" , &L , &m ) ;
  29. for( int i = ; i < n ; i ++ ) {
  30. to[ i ] = find( i ) ;
  31. }
  32. to[ n ] = n + ;
  33. block = sqrt( n ) ;
  34. num = n / block ;
  35. if( n % block ) num ++ ;
  36. for( int i = ; i <= n ; i ++ ) {
  37. belong[ i ] = ( i - ) / block + ;
  38. }
  39. for( int i = ; i < n ; i ++ ) {
  40. int t = i , sp = ;
  41. while( belong[ t ] == belong[ i ] ) {
  42. t=to[ t ] ;
  43. sp ++ ;
  44. }
  45. val[ i ] = sp ;
  46. nxt[ i ] = t ;
  47. if( i >= block * ( num - ) + ) val[ i ] -- , nxt[ i ] -- ;
  48. }
  49. for( int i = ; i <= m ; i ++ ) {
  50. int x , y , ans = ;
  51. scanf( "%d%d" , &x ,&y ) ;
  52. if( x > y )swap( x , y ) ;
  53. while( belong[ x ] < belong[ y ] ) {
  54. ans += val[ x ] ;
  55. x = nxt[ x ] ;
  56. }
  57. while( x < y ) {
  58. x = to[ x ] ;
  59. ans ++ ;
  60. }
  61. printf( "%d\n" , ans ) ;
  62. }
  63. return ;
  64. }

ARC060E


7.[AtCoder ARC061D]Snuke's Coloring

题目链接

总结:map

很明显一个点被染色只有可能对周围的点产生影响,所以暴力修改就行了,至于怎么暴力修改,当然是用$map$啊(大雾),虽然范围到$1e9$但是点只有$1e5$,所以用一个$map$存

然后0的点可以用数学方法算出来$(h-2)*(w-2)$(小学知识?

但是并不会遍历$map$所以专门去学了一下...

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define ll long long
  6.  
  7. map<pair<int,int>,int>mp;
  8.  
  9. int h , w , n , cnt[ ] ;
  10.  
  11. int main(){
  12. scanf( "%d%d%d" , &h , &w , &n ) ;
  13. while( n -- ) {
  14. int x , y ;
  15. scanf( "%d%d" , &x , &y ) ;
  16. for( int i = max( , x - ) ; i <= min( h - , x ) ; i ++ ) {
  17. for( int j = max( , y - ) ; j <= min( w - , y ) ; j ++ ) {
  18. mp[ pair<int,int>(i,j) ] ++ ;
  19. }
  20. }
  21. }
  22. map< pair<int,int> , int > :: iterator iter ;
  23. iter = mp.begin() ;
  24. while( iter != mp.end() ) {
  25. cnt[ iter -> second ] ++ ;
  26. iter ++ ;
  27. n ++ ;
  28. }
  29. ll ans = 1ll * ( h - ) * ( w - ) ;
  30. printf( "%lld\n" , ans - n - ) ;
  31. for( int i = ; i <= ; i ++ ) {
  32. printf( "%d\n" , cnt[ i ] ) ;
  33. }
  34. return ;
  35. }

ARC061D


8.[AtCoder ARC061E]Snuke's Subway Trip

题目链接

总结:spfa/dijkstra+拆边

这题我卡了两天...然后wa了要有差不多20次...不断逼近正解

首先显然是最短路,但是边权是会变的。如果你熟悉最短路的模型的话,大概是可以想出来在更新距离数组$d$的时候,顺便维护一下一个$now$数组表示上次最优解走到这里是用的哪家铁路

但是这样会挂掉1半的点:你可能有多种方法到这个点且花费相同....所以你需要开个$map$或者$set$来存当前最优解情况下能到这的点有哪些...

注意是双向边...我一开始打的单向边挂的不知所措...

细节很多,一定要注意细节

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 500010
  6. #define inf 0x3f3f3f3f
  7.  
  8. struct node {
  9. int to , nxt , belong ;
  10. }e[ N << ];
  11.  
  12. int n , m ;
  13. int head[ N << ] , cnt ;
  14.  
  15. int d[ N * ] ;
  16.  
  17. void ins( int u , int v , int c ) {
  18. e[ ++ cnt ].to = v ;
  19. e[ cnt ].nxt = head[ u ] ;
  20. e[ cnt ].belong = c ;
  21. head[ u ] = cnt ;
  22. }
  23.  
  24. std::set< int > st[ N ] ;
  25. priority_queue< pair<int,int> > q;
  26.  
  27. void spfa() {
  28. for( int i = ; i <= n ; i ++ ) d[ i ] = inf ;
  29. d[ ] = ;
  30. q.push( { , } ) ;
  31. while(!q.empty()){
  32. int u = q.top().second , ans = -q.top().first;
  33. q.pop() ;
  34. if( d[ u ] < ans ) continue ;
  35. for( int i = head[ u ] ; i ; i =e[ i ].nxt ) {
  36. int v = e[ i ].to ;
  37. int num = ans + !st[ u ].count( e[ i ].belong ) ;
  38. if( d[ v ] > num ) {
  39. d[ v ] = num ;
  40. q.push( { -num , v } ) ;
  41. st[ v ].clear() ;
  42. st[ v ].insert( e[ i ].belong ) ;
  43. }else if( d[ v ] == num ) {
  44. st[ v ].insert( e[ i ].belong ) ;
  45. }
  46. }
  47. }
  48. if( d[ n ] == inf ) puts( "-1" ) ;
  49. else printf( "%d\n" , d[ n ] ) ;
  50. }
  51.  
  52. int main() {
  53. scanf( "%d%d" , &n ,&m ) ;
  54. for( int i = ; i <= m ; i ++ ) {
  55. int x , y , c ;
  56. scanf( "%d%d%d" , &x , &y , &c ) ;
  57. ins( x , y , c ) ;
  58. ins( y , x , c ) ;
  59. }
  60. spfa() ;
  61. }

dijkstra

然后我其实一开始打的是spfa的,莫名其妙挂掉了2个点...我也不知道错在哪里...打了dijkstra就过了。代码放在这里有哪位大佬能看下我哪里出错了就在评论区说一下吧。QAQ

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 500010
  6. #define inf 0x3f3f3f3f
  7.  
  8. struct node {
  9. int to , nxt , belong ;
  10. }e[ N << ];
  11.  
  12. int n , m ;
  13. int head[ N << ] , cnt ;
  14. int d[ N * ] , vis[ N * ] ;
  15. int q[ N * ] ;
  16.  
  17. void ins( int u , int v , int c ) {
  18. e[ ++ cnt ].to = v ;
  19. e[ cnt ].nxt = head[ u ] ;
  20. e[ cnt ].belong = c ;
  21. head[ u ] = cnt ;
  22. }
  23.  
  24. std::set< int > st[ N ] ;
  25.  
  26. void spfa() {
  27. memset( d , inf , sizeof( d ) ) ;
  28. q[ ] = vis[ ] = ;
  29. d[ ] = ;
  30. int l = , r = ;
  31. while( l <= r ) {
  32. int u = q[ l ++ ] ;
  33. vis[ u ] = ;
  34. for( int i = head[ u ] ; i ; i = e[ i ].nxt ) {
  35. int v = e[ i ].to , num = d[ u ] + ( !st[ u ].count( e[ i ].belong ) ) ;
  36. if( d[ v ] > num ) {
  37. d[ v ] = num ;
  38. st[ v ].clear() ;
  39. st[ v ].insert( e[ i ].belong ) ;
  40. if( ! vis[ v ] ) vis[ v ] = , q[ r ++ ] = v ;
  41. }else if( d[ v ] == num ) {
  42. st[ v ].insert( e[ i ].belong ) ;
  43. }
  44. }
  45. }
  46. if( d[ n ] == inf ) puts( "-1" ) ;
  47. else printf( "%d\n" , d[ n ] ) ;
  48. }
  49.  
  50. int main() {
  51. scanf( "%d%d" , &n ,&m ) ;
  52. for( int i = ; i <= m ; i ++ ) {
  53. int x , y , c ;
  54. scanf( "%d%d%d" , &x , &y , &c ) ;
  55. ins( x , y , c ) ;
  56. ins( y , x , c ) ;
  57. }
  58. spfa() ;
  59. }

spfa(挂了2个点)

网上做法怎么全都是拆边的,貌似很妙的样子,我去学习一下然后来更新一下

拆边做法真的很优美呢

我上面的做法真是不优美,所以来更新一个优美一点的做法

我们把单条边拆掉,拆成三条边,中间放两个虚点,规定虚点之间的费用为0,实点到虚点的费用为1,那么就可以直接跑spfa了,最后答案除2就行(这样子拆边的话一条路径的花费是2,所以要除一下)

至于虚点的编号可以用map来取,为什么不能直接$tot++$呢?因为如果直接加点的编号,那么对于同个公司的情况就没法求了——你每一次到达的都是一个新点。

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 5000010
  6. #define inf 0x3f3f3f3f
  7.  
  8. int head[ N ] , cnt ;
  9. int d[ N ] , vis[ N ] , q[ N ] ;
  10. int n , m ;
  11. struct node {
  12. int to , nxt , v ;
  13. }e[ N ] ;
  14.  
  15. void ins( int u , int v , int w ) {
  16. e[ ++ cnt ].to = v ;
  17. e[ cnt ].nxt = head[ u ] ;
  18. e[ cnt ].v = w ;
  19. head[ u ] = cnt ;
  20. }
  21.  
  22. map<pair<int,int>,int>mp;
  23.  
  24. int tot = ;
  25.  
  26. int get_num( int x , int y ) {
  27. if( !mp.count( make_pair( x , y ) ) ) mp[ make_pair( x , y ) ] = ++tot ;
  28. return mp[ make_pair( x , y ) ] ;
  29. }
  30.  
  31. void spfa() {
  32. for( int i = ; i <= tot ; i ++ ) d[ i ] = inf ;
  33. vis[ ] = q[ ] = ;
  34. d[ ] = ;
  35. int l = , r = ;
  36. while( l < r ) {
  37. int u = q[ l ++ ] ;
  38. vis[ u ] = ;
  39. for( int i = head[ u ] ; i ; i = e[ i ].nxt ) {
  40. int v = e[ i ].to ;
  41. if( d[ v ] > d[ u ] + e[ i ].v ) {
  42. d[ v ] = d[ u ] + e[ i ].v ;
  43. if( !vis[ v ] ) vis[ v ] = , q[ r ++ ] = v ;
  44. }
  45. }
  46. }
  47. if( d[ n ] == inf ) puts( "-1" ) ;
  48. else printf( "%d\n" , d[ n ] / ) ;
  49. }
  50.  
  51. int main() {
  52. scanf( "%d%d" , &n , &m ) ;
  53. tot = n ;
  54. for( int i = ; i <= m ; i ++ ) {
  55. int x , y , c ;
  56. scanf( "%d%d%d" , &x , &y , &c ) ;
  57. int n1 = get_num( x , c ) , n2 = get_num( y , c ) ;
  58. ins( x , n1 , ) ;
  59. ins( n1 , x , ) ;
  60. ins( n1 , y , ) ;
  61. ins( y , n1 , ) ;
  62. ins( n1 , n2 , ) ;
  63. ins( n2 , n1 , ) ;
  64. }
  65. spfa() ;
  66. }

拆边+spfa


9.[AtCoder ARC061F]Card Game for Three

题目链接

总结:组合数

这$F$题好难啊...只会部分分做法,下面两个方法都是部分分做法。满分做法我去看看...会的话就补一下

部分分做法

方法1:

首先$A$能赢的条件很明显,假设在所有的牌里面取出$A$张$A$牌,$B$张$B$牌,$C$张$C$牌,那么$A$能赢当且仅当$A=n,B<m,C<k$

所以假设我们在拿出了$n$张$A$牌的情况下,中间穿插着拿了$B$张$B$牌,$C$张$C$牌,则有

$$\sum_{i=0}^{i<=m+k}C(n-1,i+n-1)*3^{m+k-i}*\sum_{j=0}^{j<=m,j<=k}C(i,j)$$

首先在$i+n-1$张牌中取$n-1$张$A$牌的方案为$C(n-1,n+i-1)$

注意:最后一张牌一定需要是$A$,所以就只能有$n-1$

而且剩下的牌数的排列为$3^{m+k-i}$,要乘上去

以及在$i+n-1$中$B$和$C$的排列为$C(i,j)$,也要乘起来

所以就得到了上面的公式

但是,上面的做法我打挂了...这个式子是对的,我算后面那个$\sum_{j=0}^{j<=m,j<=k}C(i,j)$那里我没有枚举好...查不出来啊...

我在理解了满分做法之后终于发现我挂在哪里了...

在枚举后面的东西的时候我没有分类讨论。但是改完后貌似还分少了一类...

$subtask_1$10个点我错了$2$个...然后$subtask_2$被我强行水过去$2$个点..

用$C$牌数量来分类:分为$j<k$,$j<m$,$j<i$(第三类我没分...不想去改了...)

对于第一种情况:$x=\sum_{j=0}^{j<k}C(i,j)$

对于第二种情况:$x=\sum_{j=0}^{j<m}C(i,k)$

对于第三种情况:请读者独立思考(知道有第三种情况是因为我去看了一下满分的做法...)

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. const int mod = 1e9 + ;
  6. const int N = ;
  7. #define ll long long
  8.  
  9. int n , m , k ;
  10. ll fac[ N ] , ifac[ N ] , p[ N ] ;
  11.  
  12. ll mul( ll x , ll y ) {
  13. return ( 1ll * x * y ) % mod ;
  14. }
  15.  
  16. ll add( ll x , ll y ) {
  17. return ( x + y ) % mod ;
  18. }
  19.  
  20. ll power( ll a , ll b ) {
  21. int ans = , base = a ;
  22. while( b ) {
  23. if( b& ) ans = mul( ans , base ) ;
  24. base = mul( base , base ) ;
  25. b >>= ;
  26. }
  27. return ans ;
  28. }
  29.  
  30. ll inv( ll x ) {
  31. return power( x , mod - ) % mod ;
  32. }
  33.  
  34. ll C( ll x , ll y ) {
  35. return ( fac[ x ] * ifac[ y ] % mod * ifac[ x - y ] % mod ) % mod ;
  36. }
  37.  
  38. int main() {
  39. scanf( "%d%d%d" , &n , &m , &k ) ;
  40. fac[ ] = 1ll ;
  41. p[ ] = 1ll ;
  42. for( int i = ; i < N ; i ++ ) {
  43. fac[ i ] = fac[ i - ] * i % mod ;
  44. p[ i ] = p[ i - ] * 3ll % mod ;
  45. }
  46. for( int i = ; i < N ; i ++ ) {
  47. ifac[ i ] = inv( fac[ i ] ) ;
  48. }
  49. ll ans = , x = 1ll ;
  50. n -- ;
  51. for( int i = ; i <= m + k ; i ++ ) {
  52. ans = ( ans + C( n + i , n ) * p[ m + k - i ] % mod * x ) % mod ;
  53.  
  54. for( int j = ; j <= min( i , m - ) ; j ++ ) {
  55. if( i - j < k ) x = add( x , C( i , j ) ) ;
  56. else if( i - j < m ) x = add( x , C( i , i - k + ) ) ;
  57. }
  58. }
  59. printf( "%lld\n" , add( ans , mod ) ) ;
  60. return ;
  61. }

ARC061F

方法2:

换一种想法,在前$i$张卡片中拿出$n$张$A$,$j$张$B$,$C$张$C$

则可以推出一个公式

$$\sum_{i=0}^{i<=n+m+k}\frac{(i-1)!}{(n-1)!j!C!}3^{n+k+m-i}$$

如果看得懂那个方法一的话这个公式大概也是看得懂的吧...

就是$i-1$的全排列数除掉$n-1$的全排列数和$j$的全排列数和$C$的全排列数

这个是很基础的一个组合数的常识,这样子得到的就是我们要的当前情况的方案数,记住也要把当前剩下的那些的方案数也乘上去,即$3^{n+m+k-i}$

然后这里的除法是$mod$ $m$意义下的,所以要求一下逆元,代码中的$fac[i]$即为$i!$的值,$ifac[i]$即为$i!$的逆元

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 5010
  6. #define ll long long
  7. const int mod = 1e9 + ;
  8.  
  9. int n , m , k ;
  10. ll fac[ N ] , ifac[ N ] ;
  11.  
  12. ll power( ll a ,ll b ) {
  13. ll base = a , ans = ;
  14. while( b ) {
  15. if( b& ) ans = ( ans * base ) % mod ;
  16. base = ( base * base ) % mod ;
  17. b >>= ;
  18. }
  19. return ans ;
  20. }
  21. ll mul( ll x ,ll y ) {
  22. return ( 1ll * x * y ) % mod ;
  23. }
  24.  
  25. ll inv( ll x ) {
  26. return power( x , mod - ) % mod ;
  27. }
  28.  
  29. int main() {
  30. scanf( "%d%d%d" , &n , &m , &k ) ;
  31.  
  32. fac[ ] = ;
  33. for( int i = ; i < N ; i ++ ) fac[ i ] = mul( fac[ i - ] , i ) ;
  34. for( int i = ; i < N ; i ++ ) ifac[ i ] = inv( fac[ i ] ) ;
  35.  
  36. ll ans = , sum = n + k + m ;
  37.  
  38. for( int i = n ; i <= sum; i ++ ) {// 取出n张A牌
  39. for( int j = ; j <= m ;j ++ ) {//B牌数量
  40. int C = i - n - j ;//C牌数量
  41. if( C < || C > k ) continue ;
  42. ll tmp = mul( fac[ i - ] , mul( ifac[ n - ] , mul( ifac[ j ] ,ifac[ C ] ) ) ) ;
  43. tmp = mul( tmp , power( , sum - i ) ) ;
  44. ans = ( ans + tmp ) % mod ;
  45. }
  46. }
  47.  
  48. printf( "%lld\n" , ans ) ;
  49.  
  50. }

ARC061F

钻研了很久的题解,终于差不多搞懂满分做法了...

满分做法

方法一确实是对的。这个满分做法就是用来改进那里的

观察耗时,耗时基本都是花费在枚举后面那一段,所以考虑优化那一段...(这个做法很清奇...正常写组合数不应该是优化那个式子吗...)

$$\sum_{i=0}^{i<=m+k}C(n-1,i+n-1)*3^{m+k-i}*\sum_{j=0}^{j<=m,j<=k}C(i,j)$$

还是这个式子,我们来优化掉后面那个$\sum_{j=0}^{j<=m,j<=k}C(i,j)$(就是我写错了还查不出来的那个玩意...不!我写到这里的时候忽然发现我在枚举的时候没有分类讨论!)

我们把后面那个$\sum$拆掉,分三部分讨论

假设我们现在手上有$x$张$C$牌

当$x<k$时,随便取...(C的数量都比你要取的多了所以肯定不会超限)即$\sum_{i=0}^{x<k}C(x,i)$

当$x<m$时,$C$的数量在$i$以内,同时不能取超过$k$个,即$\sum_{i=0}^{i<m}C(x,i)$

当$m<=x<m+k$时,$B,C$数量均不超过$i$,同时$B$不能取超过$m$个,$C$不能取超过$k$个,即$\sum_{i-m+1}^{i<k}C(x,i)$

然后怎么优化呢

如果你熟知杨辉三角这个东西的话,大概就会知道怎么优化了

假设我们已经知道了$i-1$时$x$的值,那么其实可以推出下面的几个式子:

情况$1$:$x=x*2$

情况$2$:$x=x*2-C(i,k)$

情况$3$:$x-x*2-C(i,k)-C(i,m)$

这个挺容易推的吧...如果部分分做法的第一种有推出来那么这个优化也就顺理成章了的样子(但是我推出来了一半...)

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. const int mod = 1e9 + ;
  6. const int N = ;
  7. #define ll long long
  8.  
  9. int n , m , k ;
  10. ll fac[ N ] , ifac[ N ] , p[ N ] ;
  11.  
  12. ll mul( ll x , ll y ) {
  13. return ( 1ll * x * y ) % mod ;
  14. }
  15.  
  16. ll add( ll x , ll y ) {
  17. return ( x + y ) % mod ;
  18. }
  19.  
  20. ll power( ll a , ll b ) {
  21. int ans = , base = a ;
  22. while( b ) {
  23. if( b& ) ans = mul( ans , base ) ;
  24. base = mul( base , base ) ;
  25. b >>= ;
  26. }
  27. return ans ;
  28. }
  29.  
  30. ll inv( ll x ) {
  31. return power( x , mod - ) % mod ;
  32. }
  33.  
  34. ll C( ll x , ll y ) {
  35. return ( fac[ x ] * ifac[ y ] % mod * ifac[ x - y ] % mod ) % mod ;
  36. }
  37.  
  38. int main() {
  39. scanf( "%d%d%d" , &n , &m , &k ) ;
  40. fac[ ] = 1ll ;
  41. p[ ] = 1ll ;
  42. for( int i = ; i < N ; i ++ ) {
  43. fac[ i ] = fac[ i - ] * i % mod ;
  44. p[ i ] = p[ i - ] * 3ll % mod ;
  45. }
  46. for( int i = ; i < N ; i ++ ) {
  47. ifac[ i ] = inv( fac[ i ] ) ;
  48. }
  49. ll ans = , x = 1ll ;
  50. n -- ;
  51. for( int i = ; i <= m + k ; i ++ ) {
  52. ans = ( ans + C( n + i , n ) * p[ m + k - i ] % mod * x ) % mod ;
  53. if( i < k ) x = ( x * 2ll ) % mod ;
  54. else if( i < m ) x = ( x * 2ll - C( i , k ) ) % mod ;
  55. else x = ( x * 2ll - C( i , k ) - C( i , m ) ) % mod ;
  56. }
  57. printf( "%lld\n" , add( ans , mod ) ) ;
  58. return ;
  59. }

ARC061F满分做法

数学题真的虐哭我...这题我研究了$3$天...


10.[AtCoder ABC110C]String Transformation

题目链接

总结:模拟

简单题啊...但是我赛时磨了要半小时?想复杂了...

其实对于每个字符,分三类讨论:

$1.$字母都没有出现过的

$2.$对于字母出现过的,如果要变成的字母和之前出现过的相同,那就可以直接跳过

$3.$对于字母出现过的,然后还和你要变成的不同,那么肯定就是$No$了...变不回去的(这个可以自己模拟一下,当时就卡在这里)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3.  
  4. const int N = 2e5+ ;
  5. char s1[ N ] , s2[ N ] ;
  6. int a[ N ] , b[ N ] ;
  7.  
  8. int main() {
  9. scanf( "%s %s" , s1 , s2 ) ;
  10. int len1 = strlen( s1 + ) , len2 = strlen( s2 + ) ;
  11. if( len1 != len2 ) return puts( "No" ) , ;
  12. for( int i = ; i <= len1 ; i ++ ) {
  13. if( !a[ s1[ i ] - 'a' ] && !b[ s2[ i ] - 'a' ] ) {
  14. a[ s1[ i ] - 'a' ] = s2[ i ] - 'a' ;
  15. b[ s2[ i ] - 'a' ] = s1[ i ] - 'a' ;
  16. }else if( a[ s1[ i ] - 'a' ] == s2[ i ] - 'a' && b[ s2[ i ] - 'a' ] == s1[ i ] - 'a' ) continue ;
  17. else if( a[ s1[ i ] - 'a' ] != s2[ i ] - 'a' || b[ s2[ i ] - 'a' ] != s1[ i ] - 'a' ) return puts("No"), ;
  18. }
  19. return puts("Yes") , ;
  20. }

ABC110C


11.[AtCoder ABC110D]Factorization

题目链接

总结:组合数,质因数分解

我们可以设$pi$为$m$的所有质因数,则有

$$m=p1^{b1}p2^{b2}...pk^{bk}$$

(质因数分解的常识)

题目的条件是$$a1a2...an=m$$

则有$$ai=p1^{c_{i,1}}p2^{c_{i,2}}...pk^{c_{i,k}}$$

但是对于$n$个$ai$而言,我们求得的这些$pi^{c_{i,j}}$肯定是有重复的,要考虑怎么筛掉这些重复的情况

我们可以设

$$b_j=c_{1,j}+c_{2,j}+c_{3,j}+...+c_{n,j}$$

因为对于一个$i$而言,$ai$和$ai'$不同,当且仅当$c_{i,j}$和$c_{i,j}'$不同

所以我们可以直接去计算这些$bj$

答案即为$$C(b_1+n-1,n-1)*C(b_2+n-1,n-1)*...*C(b_k+n-1,n-1)$$

$b_i$是$m$的质因数$pi$出现的次数

所以效率应该是$O(\sqrt{m}*log(m))$

当然也可以预处理出每个数的阶乘和逆元,那么不算预处理的话复杂度是$O(\sqrt{m})$的,但是预处理了其实会更慢一点...因为如果数组开的比较大的话预处理的时间可能会大于$\sqrt{m}$

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 1000010
  6. #define ll long long
  7.  
  8. const ll mod = 1e9 + ;
  9. ll fac[ N ] ,ifac[ N ] ;
  10. ll n , m ;
  11. ll q[ N ] , cnt = ;
  12. map<int,int> mp ;
  13.  
  14. ll power( ll a , ll b ) {
  15. ll ans = , base = a ;
  16. while( b ) {
  17. if( b& ) ans = ( ans * base ) % mod ;
  18. base = ( base * base ) % mod ;
  19. b >>= ;
  20. }
  21. return ans ;
  22. }
  23.  
  24. ll C( ll n , ll m ) {
  25. return fac[ m ] * ifac[ n ] % mod * ifac[ m - n ] % mod ;
  26. }
  27.  
  28. int main() {
  29. scanf( "%lld%lld" , &n , &m ) ;
  30. fac[ ] = ;
  31. for( int i = ; i < N ; i ++ ) fac[ i ] = ( fac[ i - ] * i ) % mod ;
  32. for( int i = ; i < N ; i ++ ) ifac[ i ] = power( fac[ i ] , mod - ) % mod ;
  33. ll ln = m , tmp = ;
  34. while( ln != && tmp * tmp <= ln ) {
  35. if( ln % tmp == ) {
  36. mp[ tmp ] ++ ;
  37. if( mp[ tmp ] == ) q[ ++ cnt ] = tmp ;
  38. ln /= tmp ;
  39. }else tmp ++ ;
  40. }
  41. if( ln != ) {
  42. mp[ ln ] ++ ;
  43. if( mp[ ln ] == ) q[ ++ cnt ] = ln ;
  44. }
  45. ll ans = ;
  46. for( int i = ; i <= cnt ; i ++ ) {
  47. ans = ans * C( n - , mp[ q[ i ] ] + n - ) % mod ;
  48. }
  49. printf( "%lld\n" , ans ) ;
  50. return ;
  51. }

ABC110D


12.[AtCoder ARC062D]AtCoDeer and Rock-Paper

题目链接

总结:结论题,模拟

就是一个只有石头和布的石头剪刀布...然后任何时候石头都得比剪刀出的次数多

假的$D$题...一个特别好猜的结论,只要能出布那就出布,因为不管怎么样出布都稳赚不赔,所以按这个结论直接模拟就好

拿两个$cur$一个维护布数一个维护石头数就行了

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4.  
  5. using namespace std ;
  6.  
  7. #define ll long long
  8. #define N 100010
  9.  
  10. char s[ N ] ;
  11. int cur1 , cur2 , ans ;
  12.  
  13. //rock和paper,paper赢
  14. //同样则不加分
  15. //rock-g , paper-p
  16.  
  17. int main() {
  18. scanf( "%s" , s + ) ;
  19. int n = strlen( s + ) ;
  20. if( s[ ] == 'p' ) ans = - ;
  21. cur2 = ;
  22. for( int i = ; i <= n ; i ++ ) {
  23. if( s[ i ] == 'p' ) {
  24. if( cur1 < cur2 ) {
  25. cur1 ++ ;
  26. }else ans -- , cur2 ++ ;
  27. }else {
  28. if( cur1 < cur2 ) {
  29. cur1 ++ ;
  30. ans ++ ;
  31. }else cur2 ++ ;
  32. }
  33. }
  34. printf( "%d\n" , ans ) ;
  35. return ;
  36. }

ARC062D


13.[AtCoder ARC062E]Building Cubes with AtCoDeer

题目链接

总结:map+hash+暴力

对于一个正方体,显然我们只要知道一个对面就能知道所有的面的颜色。

然后可以把每个正方形的四个放置方式的$hash$值存进$map$里面

接着枚举两个面(假设他们相对)然后就可以算出其他面的颜色,然后乘法原理乘一下就可以了

不好意思,$hash+map$真的是可以为所欲为的

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 1010
  6. #define ll long long
  7.  
  8. int n ;
  9. ll c[ N ][ ] ;
  10. ll h[ N ] , ans = ;
  11. map< ll , ll > mp ;
  12.  
  13. ll hash( ll *a ) {
  14. ll res = ;
  15. for( int i = ; i < ; i ++ ) {
  16. res |= ( a[ i ] << ( i * 10ll ) ) ;
  17. }
  18. return res ;
  19. }
  20.  
  21. void upd( ll x , ll d ) {
  22. for( int i = ; i < ; i ++ , x = ( ( x&1023ll ) << 30ll ) | ( x >> 10ll ) ) {
  23. mp[ x ] += d ;
  24. }
  25. }
  26.  
  27. int main() {
  28. scanf( "%d" , &n ) ;
  29. for( int i = ; i <= n ; i ++ ) {
  30. for( int j = ; j < ; j ++ ) {
  31. scanf( "%lld" , &c[ i ][ j ] ) ;
  32. }
  33. h[ i ] = hash( c[ i ] ) ;
  34. upd( h[ i ] , 1ll ) ;
  35. }
  36. for( int i = ; i <= n - ; i ++ ) {
  37. upd( h[ i ] , -1ll ) ;
  38. for( int j = i + ; j <= n ; j ++ ) {
  39. upd( h[ j ] , -1ll ) ;
  40. for( int a = ; a < ; a ++ ) {
  41. ll res = 1ll , val[ ] ;
  42. bool check = ;
  43. for( int b = ; b < ; b ++ ) {
  44. ll x[] = { c[ i ][ ( b + ) & ] , c[ i ][ b ] , c[ j ][ ( - b + a ) & ] , c[ j ][ ( - b + a ) & ] } ;
  45. val[ b ] = hash( x ) ;
  46. if( !mp.count( val[ b ] ) ) {
  47. check = ;
  48. break ;
  49. }
  50. }
  51. if( !check ) {
  52. for( int k = ; k < ; k ++ ) {
  53. res = res * mp[ val[ k ] ] ;
  54. upd( val[ k ] , -1ll ) ;
  55. }
  56. ans += res ;
  57. for( int k = ; k < ; k ++ ) {
  58. upd( val[ k ] , 1ll ) ;
  59. }
  60. }
  61. }
  62. upd( h[ j ] , 1ll ) ;
  63. }
  64. }
  65. printf( "%lld" , ans ) ;
  66. }

ARC062E


14.[AtCoder ARC063D]An Invisible Hand

题目链接

总结:递推

容易发现因为不能往回走,所以这个贸易其实就是选两个点使得他们的差值最大。要降低利润就是要么给起点$+1$要么给终点$-1$

那么我们只要扫一遍找出这个差值,再扫一遍找出差值为这个最大差值的个数就行

总的复杂度$O(n)$

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 100010
  6. #define inf 0x3f3f3f3f
  7. #define ll long long
  8.  
  9. int n , t ;
  10. int a[ N ] ;
  11.  
  12. int main() {
  13. scanf( "%d%d" , &n , &t ) ;
  14. for( int i = ; i <= n ; i ++ ) scanf( "%d" ,&a[ i ] ) ;
  15. int ans = , mx = ;
  16. int tot = ;
  17. for( int i = n ; i ; i -- ) {
  18. mx = max( a[ i ] , mx ) ;
  19. ans = max( mx - a[ i ] , ans ) ;
  20. }
  21. mx = ;
  22. for( int i = n ; i ; i -- ) {
  23. mx = max( a[ i ] , mx ) ;
  24. if( mx - a[ i ] == ans ) tot ++ ;
  25. }
  26. printf( "%d\n" , tot ) ;
  27. return ;
  28. }

ARC063D


15.[AtCoder ARC063E]Integers on a Tree

题目链接

总结:堆+递推

唔...这道题好像是挺经典的题目?(可能是我记错了)

反正就是枚举每个点,向旁边递推,他们的差值的绝对值必须为$1$,对于没有放数字的点,我们显然可以用现在正在用来推的这个点$u$来更新,但是我们怎么知道要$+1$还是$-1$呢

这时候就要用到堆了,用一个小根堆来保证我们是从小的数开始递推的,那么对于没有放数字的点,我们只要直接更新就好了$d[v]=d[u]+1$

如果发现一条路径上的两个点差值的绝对值不为$1$就直接输出$No$就行了

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 100010
  6. #define inf 0x3f3f3f3f
  7.  
  8. int n , K , d[ N ] ;
  9. int head[ N << ] , cnt , vis[ N ] ;
  10. struct edge {
  11. int to , nxt ;
  12. }e[ N << ] ;
  13. priority_queue< pair<int,int> , vector< pair<int,int> > , greater< pair<int,int> > > q ;
  14.  
  15. void ins( int u , int v ) {
  16. e[ ++ cnt ].to = v ;
  17. e[ cnt ].nxt = head[ u ] ;
  18. head[ u ] = cnt ;
  19. }
  20.  
  21. int main() {
  22. for( int i = ; i < N ; i ++ ) d[ i ] = inf ;
  23. scanf( "%d" , &n ) ;
  24. for( int i = ; i < n ; i ++ ) {
  25. int u , v ;
  26. scanf( "%d%d" , &u , &v ) ;
  27. ins( u , v ) ; ins( v , u ) ;
  28. }
  29. scanf( "%d" , &K ) ;
  30. for( int i = ; i <= K ; i ++ ) {
  31. int p , v ;
  32. scanf( "%d%d" , &v , &p ) ;
  33. d[ v ] = p ;
  34. q.push( make_pair( p , v ) ) ;
  35. }
  36. while( !q.empty() ) {
  37. int u = q.top().second , val = q.top().first ;
  38. q.pop() ;
  39. for( int i = head[ u ] ; i ; i =e[ i ].nxt ) {
  40. int v = e[ i ].to ;
  41. if( d[ v ] == inf ) d[ v ] = val + , q.push( make_pair( d[ v ] , v ) ) ;
  42. if( abs( d[ v ] - d[ u ] ) != ) {
  43. puts( "No" ) ;
  44. return ;
  45. }
  46. }
  47. }
  48. puts( "Yes" ) ;
  49. for( int i = ; i <= n ; i ++ ) {
  50. printf( "%d\n" , d[ i ] ) ;
  51. }
  52. return ;
  53. }

ARC063E


16.[AtCoder ARC064D]An Ordinary Game

题目链接

总结:结论题

结论题什么的最讨厌了!

这题结论要从奇偶性入手:

首先可以发现最后的字符串一定是形如“$ababab$”这样子由两个字符交替组成的

所以我们可以根据最后的这个字符串的奇偶性入手来判断谁赢谁输

如果字符串的第一个字符和最后一个字符相同,那么最后的字符串是奇数的,否则是偶数的

然后再根据原串的奇偶性就可以判断出答案了

神仙结论...

推不出来...

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 100010
  6. char a[ N ] ;
  7.  
  8. int main() {
  9. int cnt = ;
  10. scanf( "%s" , a + ) ;
  11. int n = strlen( a + ) , k = n & ;
  12. puts( abs( k - ( a[ n ] == a[ ] ) ) & ? "First" : "Second" ) ;
  13. }

ARC064D


17.[AtCoder ARC098D]Xor Sum 2

题目链接

总结:two-pointers

$xor$ 性质: $a\ xor\ b <= a + b $

$a\ xor\ b == a + b$ 时当且仅当 a & b = 0

所以$a_l\ xor ... xor\ a_r == a_l\ xor ... xor\ a_r$ 当且仅当a_l到a_r每一位上只有一个1

于是这个区间是有单调性的(对于合法的$a_l $和 $a_r$ , $a_{l+1}$ 和 $a_{r}$显然也合法)

所以用$two-pointers$搞搞就行了

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define ll long long
  6. #define N 200010
  7.  
  8. int n ;
  9. ll a[ N ] ;
  10.  
  11. // xor 性质: a xor b <= a + b
  12. // a xor b == a + b 时当且仅当 a & b = 0
  13. //所以a_l xor ... xor a_r == a_l xor ... xor a_r 当且仅当a_l到a_r每一位上只有一个1
  14. //于是这个区间是有单调性的(对于合法的a_l 和 a_r , a_{l+1} 和 a_{r} 显然也合法)
  15. //所以用two-pointers搞搞
  16.  
  17. int main() {
  18. scanf( "%d" , &n ) ;
  19. for( int i = ; i <= n ; i ++ ) {
  20. scanf( "%lld" , &a[ i ] ) ;
  21. }
  22. int l = , r = , now = ;
  23. ll ans = ;
  24. while( r <= n ) {
  25. while( ( now ^ a[ r ] ) == now + a[ r ] && r <= n ) now |= a[ r ++ ] , ans += r - l ;
  26. now ^= a[ l ++ ] ;
  27. }
  28. printf( "%lld\n" , ans ) ;
  29. }

ARC098D


18.[AtCoder ARC066C]C - Lining Up

题目链接

总结:结论题

这个atcoder怎么天天结论题

分类讨论一下

首先不管怎么样每个数都不能出现超过两次(左边一个右边一个然后就没了),否则就是不合法状态

对于长度为偶数的情况,显然0不会出现

对于长度为奇数的情况,显然0只会出现一次

所以把不合法状态判一下,答案就是$2^{n/2}$(每对数两种排列)

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. const int N = 1e5 + ;
  6. const int mod = 1e9+ ;
  7. #define ll long long
  8.  
  9. int n ;
  10. int a[ N ] ;
  11. int cnt[ N ] ;
  12. ll ans = ;
  13.  
  14. ll power( int a , int b ) {
  15. ll ans = , base = a ;
  16. while( b ) {
  17. if( b & ) ans = ans * base % mod ;
  18. base = base * base % mod ;
  19. b >>= ;
  20. }
  21. return ans ;
  22. }
  23.  
  24. int main() {
  25. scanf( "%d" , &n ) ;
  26. for( int i = ; i <= n ; i ++ ) scanf( "%d" , &a[ i ] ) , cnt[ a[ i ] ] ++ ;
  27. for( int i = ; i < n ; i ++ ) {
  28. if( cnt[ i ] > ) return puts( "" ) , ;
  29. }
  30. if( n & && cnt[ ] != ) return puts( "" ) , ;
  31. if( !( n & ) && cnt[ ] ) return puts( "" ) , ;
  32. printf( "%lld\n" , power( , n / ) ) ;
  33. }

ARC066C


19.[AtCoder ARC070C]Go Home

题目链接

总结:结论题

简单结论题

答案其实就是$\sum_{i=1}^{n}i>=x$的$n$的最小值

因为如果你跳出去了,跳出去的距离一定是之前跳过的

所以之前那一次不跳就可以了

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. int x ;
  6.  
  7. int main() {
  8. scanf( "%d" , &x ) ;
  9. int sum = ;
  10. for( int i = ; ; i ++ ) {
  11. sum += i ;
  12. if( sum >= x ) {
  13. printf( "%d\n" , i ) ;
  14. return ;
  15. }
  16. }
  17. }

ARC070C


20.[AtCoderARC071C]Dubious Document

题目链接

总结:模拟

最后一题了..随便放个简单题吧

就是对于每个字母,它输出的次数就是它在所有字符串中出现的最小次数

开个map随便搞搞就行了

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. int n ;
  6. map< char , int > cnt[ ] ;
  7. map< int , int > ans ;
  8. char s[ ][ ] ;
  9.  
  10. int main() {
  11. scanf( "%d" , &n ) ;
  12. for( int i = ; i <= n ; i ++ ) {
  13. scanf( "%s" , s[ i ] + ) ;
  14. for( int j = , len = strlen( s[ i ] + ) ; j <= len ; j ++ ) {
  15. cnt[ i ][ s[ i ][ j ] ] ++ ;
  16. }
  17. }
  18. for( int i = ; i < ; i ++ ) {
  19. int m = 1e10 ;
  20. for( int j = ; j <= n ; j ++ ) {
  21. m = min( m , cnt[ j ][ (char)(i+'a') ] ) ;
  22. }
  23. ans[ i ] = m ;
  24. }
  25. for( int i = ; i < ; i ++ ) {
  26. for( int j = ; j <= ans[ i ] ; j ++ ) {
  27. putchar( i + 'a' ) ;
  28. }
  29. }
  30. ans.clear() ;
  31. for( int i = ; i < ; i ++ ) {
  32. int m = 1e10 ;
  33. for( int j = ; j <= n ; j ++ ) {
  34. m = min( m , cnt[ j ][ (char)( i + 'A' ) ] ) ;
  35. }
  36. ans[ i ] = m ;
  37. }
  38. for( int i = ; i < ; i ++ ) {
  39. for( int j = ; j <= ans[ i ] ; j ++ ) {
  40. putchar( i + 'A' ) ;
  41. }
  42. }
  43. return ;
  44. }

AtCoder 杂题训练的更多相关文章

  1. Atcoder&CodeForces杂题11.7

    Preface 又自己开了场CF/Atcoder杂题,比昨天的稍难,题目也更有趣了 昨晚炉石检验血统果然是非洲人... 希望这是给NOIP2018续点rp吧 A.CF1068C-Colored Roo ...

  2. dp杂题(根据个人进度选更)

    ----19.7.30 今天又开了一个新专题,dp杂题,我依旧按照之前一样,这一个专题更在一起,根据个人进度选更题目; dp就是动态规划,本人认为,动态规划的核心就是dp状态的设立以及dp转移方程的推 ...

  3. 贪心/构造/DP 杂题选做Ⅲ

    颓!颓!颓!(bushi 前传: 贪心/构造/DP 杂题选做 贪心/构造/DP 杂题选做Ⅱ 51. CF758E Broken Tree 讲个笑话,这道题是 11.3 模拟赛的 T2,模拟赛里那道题的 ...

  4. 正睿OI DAY3 杂题选讲

    正睿OI DAY3 杂题选讲 CodeChef MSTONES n个点,可以构造7条直线使得每个点都在直线上,找到一条直线使得上面的点最多 随机化算法,check到答案的概率为\(1/49\) \(n ...

  5. wangkoala杂题总集(根据个人进度选更)

    CQOI2014 数三角形 首先一看题,先容斥一波,求出网格内选三个点所有的情况,也就是C(n*m,3);然后抛出行里三点共线的方案数:C(n,3)*m; 同理就有列中三点共线的方案数:n*C(m,3 ...

  6. python+java蓝桥杯ACM日常算法题训练(一)10基础题

    目录 1.简单的a+b 2.第一个HelloWorld程序! 3.三个数最大值 4.密码破译 5.母牛的故事 6.7.8.9.10 @(这里写自定义目录标题) 算法题训练网站:http://www.d ...

  7. 2019暑期金华集训 Day6 杂题选讲

    自闭集训 Day6 杂题选讲 CF round 469 E 发现一个数不可能取两次,因为1,1不如1,2. 发现不可能选一个数的正负,因为1,-1不如1,-2. hihoCoder挑战赛29 D 设\ ...

  8. mysql常用语句及实题训练

    基本语句操作 创建数据库: create database database-name 1 删除数据库: drop database database-name 1 修改数据名: RENAME DAT ...

  9. Codeforces 杂题集 2.0

      记录一些没有写在其他随笔中的 Codeforces 杂题, 以 Problemset 题号排序   1326D2 - Prefix-Suffix Palindrome (Hard version) ...

随机推荐

  1. Java并发包中CyclicBarrier的源码分析和使用

    CyclicBarrier的介绍和源码分析 CyclicBarrier的字母意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫做同步点)时被阻塞 ...

  2. 流媒体ts/ps流封装/分析

    1.TS 1) 感谢星辰同学,还热乎着,

  3. PAT 1028 List Sorting[排序][一般]

    1028 List Sorting (25)(25 分) Excel can sort records according to any column. Now you are supposed to ...

  4. 【Cocos2dx 3.3】图片裁剪

        从一个图片集中裁剪出需要的图片时,采用的坐标是屏幕坐标系:        示例如下:    图片:res/Images/grossini_dance_atlas.png ,每个人物大小为85* ...

  5. (转)mysql数据文件解析

    一 数据文件 在 MySQL中每一个数据库都会在定义好(或者默认)的数据目录下存在一个以数据库名字命名的文件夹,用来存放该数据库中各种表数据文件.不同的 MySQL存储引擎有各自不同的数据文件,存放位 ...

  6. 7.10 Models -- Handling Metadata(处理元数据)

    1. 随着从store中返回的records,你可能需要处理一些元数据.Metadata是伴随着特定model或者type的一种数据,而不是record. 2. 分页是使用元数据的一个常见的例子.想象 ...

  7. sql中字符串如何比大小

    从字符串的第一个字符开始比较ASSCII码值,如果相等则看下一个,以此类推. 数字的ASCII码<大写字母的ASCII码<小写字母的ASCII码. ASCII码

  8. 使用Linux工作之Fedora KDE

    小明拿着在Windows下不断蓝屏的T440和公司建议不使用云笔记的规定,心下想着,是时候回归linux了... 一.系统的获取与启动盘的制作 fedora20 KDE版 liveusb-creato ...

  9. UVA 103

    /* 这题说的的是 N 维的坐标, 每个盒子的N维坐标 可以进行 随意方式的调换 然后求出 A全部的坐标小于B的 则 A 可以嵌套在B中 然后 计算出最多的 盒子嵌套个数 简单的状态转移 我为何如此的 ...

  10. POJ 分类

    初期: 一.基本算法:      (1)枚举. (poj1753,poj2965)      (2)贪心(poj1328,poj2109,poj2586)      (3)递归和分治法.      ( ...