AtCoder Regular Contest 149
A. Repdigit Number ∘ \color{#007f00}\texttt{A. Repdigit Number}\color{red}~\circ A. Repdigit Number ∘
/*
name: Repdigit Number
id: AT_arc149_a
date: 2023/01/23
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, a, b;
int main(){
scanf("%d%d", &n, &m);
ll p = 0;
for(int i = 1; i <= n; ++ i){
p = (p * 10 + 1);
ll r = p % m;
p %= m;
for(int j = 1; j <= 9; ++ j){
if(r * j % m == 0){
a = i, b = j;
}
}
}
if(!a){
puts("-1");
} else {
for(int i = 1; i <= a; ++ i){
putchar(b + '0');
}
puts("");
}
return 0;
}
B. Two LIS Sum ∘ \color{#007f00}\texttt{B. Two LIS Sum}\color{red}~\circ B. Two LIS Sum ∘
/*
name: Two LIS Sum
id: AT_arc149_b
date: 2023/01/24
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int n, a[N], b[N], st[N], top;
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &b[i]);
}
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[b[i]]);
}
st[++top] = a[1];
for(int i = 2; i <= n; ++ i){
if(st[top] < a[i]){
st[++top] = a[i];
} else {
int k = lower_bound(st + 1, st + top + 1, a[i]) - st;
st[k] = a[i];
}
}
printf("%d\n", top + n);
return 0;
}
C. Avoid Prime Sum ∘ \color{#00B0B0}\texttt{C. Avoid Prime Sum}\color{red}~\circ C. Avoid Prime Sum ∘
显然奇+奇为合数,偶+偶为合数,所以我们可以将上半部分填满奇数,下半部分填满偶数,然后只用考虑两部分交界的地方了。
如果 n n n 为偶数,那么可以直接枚举出来一个值域在 [ 2 n , n 2 ] [2n, n^2] [2n,n2] 的奇合数,然后把相加为这个数的二元组填到交界处。
如果 n n n 为奇数,那么边界会在中间有个拐弯,那么就在中间构造一个一下结构( 1 1 1 在正中心):
7 | ||
---|---|---|
8 | 1 | |
14 |
3 ∗ 3 3*3 3∗3 没有 14 14 14,那就特判一下。
那么接着和上面一样填就好了。注意的是枚举奇合数从小往大可能会有问题(已经用了四个小的数),改成从大往小枚举就行了。
/*
name: Avoid Prime Sum
id: AT_arc149_c
date: 2023/01/24
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, vis[N*N], a[N][N];
bool isp(int k){
for(int i = 2; i * i <= k; ++ i){
if(k % i == 0){
return false;
}
}
return true;
}
int main(){
scanf("%d", &n);
if(n == 3){
a[1][1] = 1;
a[1][2] = 9;
a[1][3] = 3;
a[2][1] = 8;
a[2][2] = 7;
a[2][3] = 5;
a[3][1] = 6;
a[3][2] = 2;
a[3][3] = 4;
} else if(n & 1){
int k = n / 2 + 1;
a[k][k] = 1;
a[k][k-1] = 8;
a[k-1][k-1] = 7;
a[k+1][k] = 14;
vis[1] = vis[7] = vis[8] = vis[14] = 1;
for(int i = n * n; i >= n + n; -- i){
if((i & 1) && !isp(i)){
int now = 2;
for(int j = 1; j <= n; ++ j){
int pos = j < k ? k-1 : k;
if(a[pos][j]) continue;
while(vis[now] || vis[i-now]) ++ now;
if(now & 1){
a[pos][j] = now;
a[pos+1][j] = i - now;
} else {
a[pos][j] = i - now;
a[pos+1][j] = now;
}
vis[now] = vis[i-now] = 1;
}
break;
}
}
int tot1 = 1, tot2 = 2;
for(int i = 1; i < k; ++ i){
for(int j = 1; j <= n; ++ j){
if(!a[i][j]){
while(vis[tot1]){
tot1 += 2;
}
a[i][j] = tot1;
vis[tot1] = 1;
}
}
}
for(int i = k + 1; i <= n; ++ i){
for(int j = 1; j <= n; ++ j){
if(!a[i][j]){
while(vis[tot2]){
tot2 += 2;
}
a[i][j] = tot2;
vis[tot2] = 1;
}
}
}
} else {
for(int i = n + n; i <= n * n; ++ i){
if((i & 1) && !isp(i)){
for(int j = 1; j <= n; ++ j){
if(j & 1){
a[n/2][j] = j;
a[n/2+1][j] = i - j;
} else {
a[n/2][j] = i - j;
a[n/2+1][j] = j;
}
vis[j] = vis[i-j] = 1;
}
break;
}
}
int tot1 = 1, tot2 = 2;
for(int i = 1; i < n / 2; ++ i){
for(int j = 1; j <= n; ++ j){
if(!a[i][j]){
while(vis[tot1]){
tot1 += 2;
}
a[i][j] = tot1;
vis[tot1] = 1;
}
}
}
for(int i = n / 2 + 2; i <= n; ++ i){
for(int j = 1; j <= n; ++ j){
if(!a[i][j]){
while(vis[tot2]){
tot2 += 2;
}
a[i][j] = tot2;
vis[tot2] = 1;
}
}
}
}
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= n; ++ j){
printf("%d ", a[i][j]);
}
puts("");
}
return 0;
}
D. Simultaneous Sugoroku △ \color{#FF0000}\texttt{D. Simultaneous Sugoroku}\color{red}~\vartriangle D. Simultaneous Sugoroku △
考虑求解区间 [ 1 , 1 0 6 ] [1,10^6] [1,106] 的答案。
发现如果某个时刻,两个点所在位置互为相反数,那么两个点的答案也肯定互为相反数。所以我们可以在每次移动区间的时候只保留原点右侧或左侧的一个区间,易得这个区间是连续的。最后计算完 dfs 一下就能得到每个点的结果。
/*
name: Simultaneous Sugoroku
id: AT_arc149_d
date: 2023/01/24
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10, M = 1e6 + 10;
int n, m, x[N], d, ans[M], stop[M];
vector<int> g[M];
void dfs(int x, int v, int op){
stop[x] = op;
ans[x] = v;
for(int i : g[x]){
dfs(i, -v, op);
}
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++ i){
scanf("%d", &x[i]);
}
int l = 1, r = 1000000, tag = 0;
for(int i = 1; i <= m; ++ i){
scanf("%d", &d);
int mid;
tag += d * (l + tag > 0 ? -1 : 1);
mid = -tag;
if(l <= mid && mid <= r){
stop[mid] = i;
if(mid - l > r - mid){
for(int j = r; j > mid; -- j){
g[mid*2-j].push_back(j);
}
r = mid - 1;
} else {
for(int j = l; j < mid; ++ j){
g[mid*2-j].push_back(j);
}
l = mid + 1;
}
}
}
for(int i = 1; i <= 1000000; ++ i){
if(stop[i]){
dfs(i, 1, stop[i]);
}
}
for(int i = l; i <= r; ++ i){
ans[i] = i + tag;
dfs(i, ans[i], 0);
}
for(int i = 1; i <= n; ++ i){
if(stop[x[i]]){
printf("Yes %d\n", stop[x[i]]);
} else {
printf("No %d\n", ans[x[i]]);
}
}
return 0;
}
E. Sliding Window Sort △ \color{#FF0000}\texttt{E. Sliding Window Sort}\color{red}~\vartriangle E. Sliding Window Sort △
首先把问题转化为:把 a 0 , 1 , . . . , m − 2 a_{0,1,...,m-2} a0,1,...,m−2 作为一个集合, a m − 1 , m , . . . , n a_{m-1,m,...,n} am−1,m,...,n 作为队列,进行 k k k 次操作每次把队首放入集合,并取出集合内最小值放入队尾。
设 r = n − m + 1 r=n-m+1 r=n−m+1,则若 k = r k=r k=r, k k k 次操作后左边集合变为 { r + 1 , r + 2 , . . . , n } \{r+1,r+2,...,n\} {r+1,r+2,...,n},右边记为 ( x 1 , x 2 , . . . , x r ) (x_1,x_2,...,x_r) (x1,x2,...,xr)。如果左边集合不为 { r + 1 , r + 2 , . . . , n } \{r+1,r+2,...,n\} {r+1,r+2,...,n} 则无解。
接下来考虑 x x x 数组,如果存在 x i − 1 > x i x_{i-1}>x_i xi−1>xi,那么这个 x i x_i xi 就只有一种可能位置(因为此时的集合的数必定都大于 x i − 1 x_{i-1} xi−1,从而大于 x i x_i xi)。否则有 1 + ( m − 1 ) = m 1+(m-1)=m 1+(m−1)=m 种可能位置。
因为集合是无序的,答案应该乘上 ( m − 1 ) ! (m-1)! (m−1)!。
如果 k > r k>r k>r,那么 [ r + 1 , r + 2 , . . . , k ] [r+1,r+2,...,k] [r+1,r+2,...,k] 的操作其实相当于对于右边的队列循环左移,可以转化为 k = r k=r k=r 的问题。
如果 k < r k<r k<r,那么在队列尾段的一段数是不用考虑的,也可以转化为 k = r k=r k=r 的问题。
/*
name: Sliding Window Sort
id: AT_arc149_e
date: 2023/01/24
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
const int P = 998244353;
int n, m, k, r, ans = 1, a[N], b[N];
int main(){
scanf("%d%d%d", &n, &m, &k);
r = n - m + 1;
for(int i = 0; i < n; ++ i){
scanf("%d", &a[i]);
}
rotate(a, a + k % n, a + n);
for(int i = m - 1; i <= n; ++ i){
b[i-(m-1)] = a[i];
}
if(k > r){
rotate(b, b + (r - k % r) % r, b + r);//左移
k = r;
}
for(int i = 1; i < m - 1; ++ i){//a不递增
if(a[i] < a[i-1]){
puts("0");
return 0;
}
}
for(int i = 0; i < k; ++ i){//b有贡献的部分不全小于a
if(a[0] < b[r-1-i]){
puts("0");
return 0;
}
}
for(int i = 1; i < m; ++ i){
ans = (ll) ans * i % P;
}
for(int i = r-k, las = 0; i < r; ++ i){
if(las < b[i]){
las = b[i];
ans = (ll) ans * m % P;
}
}
printf("%d\n", ans);
return 0;
}