比赛传送门 
  A. Cowardly Rooks 
题意:有一个 n × n n\times n n × n   的棋盘,有 m m m   个位置上有车,保证互不攻击。问是否能将一个车移动一次使得仍然互不攻击。
 
稍加思考可得,如果 m ≠ n m\ne n m  = n  ,一定可以,否则一定不可以。因为如果 m < n m<n m < n  ,则一定有一列是空着的,随便选一辆车移动到该列即可。如果 m = n m=n m = n  ,易证一定不可以。
By caoxuanming 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <bits/stdc++.h>  using  namespace  std;signed  main ()   {    int  t;     cin>>t;     while (t--) {         int  n,m,x,y;         cin>>n>>m;         for (int  i=1 ;i<=m;i++)             cin>>x>>y;         if (n==m) cout<<"NO" <<endl;         else  cout<<"YES" <<endl;     }     return  0 ; } 
 
  B. Death’s Blessing 
题意:有 n n n   个怪物排成一排,每个怪物有 a i , b i a_i,b_i a i  , b i    两个权值。杀掉一个怪物需要花费 a i a_i a i    的代价,同时会导致与其相邻的怪物的 a a a   增加 b i b_i b i   。杀掉后左右两边变成相邻的。求全部杀完的最小代价。
 
我们从贡献的角度考虑。显然每个怪物的 a i a_i a i    都会且仅会给答案造成 a i a_i a i    的贡献,所以可以不用考虑。对于 b i b_i b i   ,只有杀掉的时候会产生贡献,所以只会贡献一“次”,而每次可以造成 0/1/2 倍的贡献(根据邻居数量)。显然,0 倍贡献只有最后一个怪物可以达到,而其他的怪物要么 1 倍要么 2 倍。此时显然每个其他怪物都用 1 倍更优,而这种方案是一定能够达到的:只需要从两边依次取到最后一个怪物的位置即可。所以只需要让 b b b   最大的怪物造成 0 倍,其他造成 1 倍即可。
By caoxuanming 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <bits/stdc++.h>  using  namespace  std;#define  int long long int  a[200010 ],b[200010 ];signed  main ()   {    int  t;     cin>>t;     while (t--) {         int  n;         cin>>n;         for (int  i=1 ;i<=n;i++)             cin>>a[i];         for (int  i=1 ;i<=n;i++)             cin>>b[i];         int  maxn=0 ,sum=0 ;         for (int  i=1 ;i<=n;i++) {             maxn=max (maxn,b[i]);             sum+=a[i]+b[i];         }         cout<<sum-maxn<<endl;     }     return  0 ; } 
 
  C. Number Game 
题意:有一个长度为 n n n   的序列 a a a  ,进行 k k k   次操作。第一次操作 Alice 可以删掉一个 ≤ k \le k ≤ k   的数并由 Bob 再删掉一个数(如果没有可以不删);第二次操作 Alice 可以删掉一个 ≤ k − 1 \le k-1 ≤ k − 1   的数并由 Bob 再删掉一个数(如果没有可以不删),以此类推直到第 k k k   次操作删掉一个 ≤ 1 \le 1 ≤ 1   的数。如果在某次操作中 Alice 无数可删则 Bob 赢,否则 Alice赢。问 Alice 能赢的最大的 k k k  。
 
显然 k k k   不会超过 n n n  ,于是从 1 1 1   到 n n n   枚举 k k k  ,每次模拟 k k k   次操作即可。贪心地考虑,Alice 每次一定选能选的最大的数删(因为以后可能就没法删了),而 Bob 每次一定选最小的数删(最小的威胁最大,因为更容易被 Alice 删掉)。于是我们在排好序的数组上维护一个双指针即可。
By gyh20 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int  t,n,m,a[1000002 ],A,B,R[1000002 ],stk[1000002 ],tp;char  s[1000002 ];long  long  ans;inline  bool  ck (re int  x)  {    int  pos=n;     while (a[pos]>x)--pos;     for (re int  i=1 ;i<=x;++i){         while (a[pos]>x-i+1 )--pos;         if (pos<i)return  0 ;--pos;     }return  1 ; } int  main ()  {    t=read ();     while (t--){         n=read ();ans=0 ;         for (re int  i=1 ;i<=n;++i)a[i]=read ();sort (a+1 ,a+n+1 );         for (re int  i=n;~i;--i)             if (ck (i)){                 printf ("%d\n" ,i);                 break ;             }     } } 
 
  D. Counting Arrays 
题意:假设有一个序列 a 1 ∼ a k a_1\sim a_k a 1  ∼ a k   ,我们可以以某种顺序依次删除这 k k k   个元素,要求每次删除的数与“当前它所处的位置”互质。问有多少个长度在 [ 1 , n ] [1,n] [ 1 , n ]  ,值域在 [ 1 , m ] [1,m] [ 1 , m ]   的序列,满足有 ≥ 2 \ge 2 ≥ 2   种满足条件的删除方式。
 
首先,由于任何序列都存在一种平凡的满足条件的删除方式———每次都删除第一个元素,所以问题转化为有多少个序列存在一个非平凡的删除方式。进一步,我们可以发现,如果存在一个 2 ≤ j ≤ i 2\le j\le i 2 ≤ j ≤ i   使得 a i a_i a i    与 j j j   互质,则一定存在一个非平凡的删除方式:先取若干次第一个元素直到 a i a_i a i    成为第 j j j   位,然后取 a i a_i a i   ,然后再取第一个元素直到取完。反之,如果对于每个 i i i   都不存在一个 2 ≤ j ≤ i 2\le j\le i 2 ≤ j ≤ i   使得 a i a_i a i    与 j j j   互质,则一定不存在非平凡的删除方式,证明显然。
我们可以反向考虑问题,考虑有多少种序列不存在非平凡的删除方式,则每位互相独立。我们只需要对于每个 i i i  ,求出有多少种 1 ≤ a i ≤ m 1\le a_i\le m 1 ≤ a i  ≤ m   满足它与任意 2 ≤ j ≤ i 2\le j\le i 2 ≤ j ≤ i   都不互质,每位的答案乘起来即可。而一个数与任意 2 ≤ j ≤ i 2\le j\le i 2 ≤ j ≤ i   都不互质又可以进一步转化为与任意 2 ≤ j ≤ i , j  is prime 2\le j\le i,j\text{ is prime} 2 ≤ j ≤ i , j  is prime   都不互质,即是所有 ≤ i \le i ≤ i   的质数的倍数,也即:是当前所有质数乘积的倍数。所以我们可以维护一个当前质数的乘积 n o w now n o w  ,每次更新,答案便乘上 m / n o w m/now m / n o w  。由于我们求的是“不存在删除方式”的方案数,而需要求“存在”的方案数,所以真正的答案要用总方案数减去“不存在”的方案数。由于要求长度在 [ 1 , n ] [1,n] [ 1 , n ]   之间,每次扫到一个位就将总答案加上这个位数的答案即可。
By cxm1024 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <bits/stdc++.h>  using  namespace  std;#define  int long long const  int  mod=998244353 ;bool  isprime[300010 ];void  init ()   {    int  n=300010 ;     for (int  i=0 ;i<=n;i++)         isprime[i]=1 ;     isprime[0 ]=isprime[1 ]=0 ;     for (int  i=2 ;i<=n;i++) {         if (!isprime[i]) continue ;         for (int  j=i*i;j<=n;j+=i)             isprime[j]=0 ;     } } signed  main ()   {    init ();     int  n,m;     cin>>n>>m;     int  now=1 ,ans=1 ,ansans=0 ,mi=1 ;     for (int  i=1 ;i<=n;i++) {         if (now<=m&&isprime[i]) now=now*i;         (ans*=m/now%mod)%=mod;         mi=mi*(m%mod)%mod;         (ansans+=(mi-ans+mod)%mod)%=mod;     }     cout<<ansans<<endl;     return  0 ; } 
 
  E. Cactus Wall 
题意:有一个 n × m n\times m n × m   的矩阵,有些位置是有障碍的。你需要添加尽可能少的障碍,满足障碍之间不能边相邻(但可以角相邻,保证初始矩阵满足条件),使得第一行到最后一行不存在路径(不连通)。
 
我们发现,最终的墙一定是由若干个对角相邻的障碍连起来的。我们可以预处理哪些位置不能放障碍(即与已有障碍相邻的位置),然后预处理这个障碍可以与哪些位置在答案的墙中相邻(即建图),然后问题转化为从第一列到最后一列建一个墙(路径)使得添加的障碍数尽可能少(最短路)。我们可以建一个虚点作为源点连向第一列的所有能放障碍的点,并建一个虚点作为汇点连着最后一列的点,跑一遍 BFS/Dijkstra 即可。
By SSRS_ 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 #include  <bits/stdc++.h>  using  namespace  std;vector<int > dx = {1 , 0 , -1 , 0 }; vector<int > dy = {0 , 1 , 0 , -1 }; vector<int > dx2 = {1 , 1 , -1 , -1 }; vector<int > dy2 = {1 , -1 , 1 , -1 }; const  int  INF = 100000000 ;int  main ()  {  ios::sync_with_stdio (false );   cin.tie (nullptr );   int  t;   cin >> t;   for  (int  i = 0 ; i < t; i++){     int  n, m;     cin >> n >> m;     vector<string> s (n)  ;     for  (int  j = 0 ; j < n; j++){       cin >> s[j];     }     vector<vector<bool >> ok (n, vector<bool >(m, true ));     for  (int  j = 0 ; j < n; j++){       for  (int  k = 0 ; k < m; k++){         if  (s[j][k] == '#' ){           ok[j][k] = false ;           for  (int  l = 0 ; l < 4 ; l++){             int  x = j + dx[l];             int  y = k + dy[l];             if  (0  <= x && x < n && 0  <= y && y < m){               ok[x][y] = false ;             }           }         }       }     }     vector<vector<int >> d (n, vector<int >(m, INF));     vector<vector<pair<int , int >>> pr (n, vector<pair<int , int >>(m));     deque<tuple<int , int , int >> dq;     for  (int  j = 0 ; j < n; j++){       if  (s[j][0 ] == '#' ){         d[j][0 ] = 0 ;         dq.push_front (make_tuple (0 , j, 0 ));       } else  if  (ok[j][0 ]){         d[j][0 ] = 1 ;         dq.push_back (make_tuple (1 , j, 0 ));       }     }     while  (!dq.empty ()){       int  c = get<0 >(dq.front ());       int  x = get<1 >(dq.front ());       int  y = get<2 >(dq.front ());       dq.pop_front ();       if  (d[x][y] == c){         for  (int  j = 0 ; j < 4 ; j++){           int  x2 = x + dx2[j];           int  y2 = y + dy2[j];           if  (0  <= x2 && x2 < n && 0  <= y2 && y2 < m){             if  (s[x2][y2]  == '#' ){               if  (d[x2][y2] > d[x][y]){                 d[x2][y2] = d[x][y];                 pr[x2][y2] = make_pair (x, y);                 dq.push_front (make_tuple (c, x2, y2));               }             }             if  (ok[x2][y2]){               if  (d[x2][y2] > d[x][y] + 1 ){                 d[x2][y2] = d[x][y] + 1 ;                 pr[x2][y2] = make_pair (x, y);                 dq.push_back (make_tuple (c + 1 , x2, y2));               }             }           }         }       }     }     int  mn = INF, p = -1 ;     for  (int  j = 0 ; j < n; j++){       if  (d[j][m - 1 ] < mn){         mn = d[j][m - 1 ];         p = j;       }     }     if  (p == -1 ){       cout << "NO"  << "\n" ;     } else  {       vector<string> ans = s;       int  x = p, y = m - 1 ;       int  CNT = 0 ;       while  (true ){         ans[x][y] = '#' ;         if  (y == 0 ){           break ;         }         int  x2 = pr[x][y].first;         int  y2 = pr[x][y].second;         x = x2;         y = y2;       }       cout << "YES"  << "\n" ;       for  (int  j = 0 ; j < n; j++){         cout << ans[j] << "\n" ;       }     }   } }