比赛链接:https://codeforces.com/contest/1333
A. Little Artem
题意:B是有白格子相邻的黑格子数,W是有黑格子相邻的白格子数,合法涂色方案为B=W+1。
刚开始看错题意,以为是给定矩阵和黑白格子数构造合法方案,心想好像有点难,就跑去做B题了。回来发现原来只给定了矩阵,而且$2 \leq n,m \leq 100$,那么只需要使一个角落的格子为白色其余格子全为黑色即可。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 2e5 + 117;
const int MAXM = 2e5 + 117;
int t;
int n, m;
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(i == 0 && j == 0) putchar('W');
else putchar('B');
}
putchar(10);
}
}
return 0;
}
B. Kind Anton
题意:给定a、b序列,$-1 \leq a_i \leq 1,-10^9 \leq b_i \leq 10^9$,若$j<i$则可以进行操作$a_j=a_j+a_i$,问能不能使a序列变成b序列。
基本思路:从后往前操作,若$b[i]<a[i]$,要使$a[i]$变小则在$a[i]$的前面必须要存在-1,若不存在-1则不合法。同理若$b[i]>a[i]$,要使$a[i]$变大则在$a[i]$的前面必须要存在1,否则也不合法。每一次都要往前找是否存在1或者-1,则记住最左边的1和-1的位置每一次就可以$O(1)$的判断了。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 2e5 + 117;
const int MAXM = 2e5 + 117;
int t;
int n, fu, one;
int a[MAXN], b[MAXN];
int main() {
scanf("%d", &t);
while(t--) {
fu = one = INF;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
if(a[i] == 1 && one == INF) one = i;
if(a[i] == -1 && fu == INF) fu = i;
}
bool pr = true;
for(int i = 0; i < n; i++) {
scanf("%d", &b[i]);
if(b[i] > a[i] && one >= i) pr = false;
if(b[i] < a[i] && fu >= i) pr = false;
}
if(pr) puts("YES");
else puts("NO");
}
return 0;
}
C. Eugene and an array
题意:如果一个序列的所有非空子序列的和都不为零,则称该序列是一个好序列。现给定一个序列,求有多少个非空子序列是好序列。
基本思路:枚举右端点,对于每一个右端点求有多少个左端点满足要求。注意到枚举右端点已经有$O(n)$的复杂度了,那么在求左端点数量的时候需要一定的效率。
限制一:对于任意的$j$,若区间$[i,j]$的和为0,则区间$[i,j]$不是一个好序列,区间$[i-1,j]$也不是一个好序列,因为存在子序列的和为0。也就是说我们只要找到最大的$i$满足$[i,j]$的和为0,则左端点小于$i$的区间都不是好序列,左端点大于$i$的区间都是好序列。那么问题是如何快速的寻找到最大的$i$?
前缀和:设$sum[i]$表示序列的前$i$项和,若$sum[i]=sum[j]$,则区间$[i,j]$的和等于$sun[j]-sum[i]=0$。那么问题由求某段区间的和为0转化为求前缀和相同的位置,我们可以使用map<value,id>
或者离散化后二分来得到某一个前缀和最后一次在哪个位置出现,单次查询的复杂度都是$O(log_{2}{n})$。
限制二:若区间$[i,j]$的子区间$[k,l]$的和为0,则区间$[i,j]$不是一个好区间。也就是说对于任意的$j$左端点在往左拓展的时候不仅要考虑本身的区间和是否为0,还要考虑是否会包含进和为0的子区间。那么我们找到左边区间和为0的区间中最右边的一个,假设是$[k,l]$,那么$j$的左端点最多拓展到$k+1$。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 2e5 + 117;
const int MAXM = 2e5 + 117;
int n;
int last;
LL ans, a[MAXN];
map<LL, int> id;
int main() {
id[0] = 1;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
a[i] += a[i - 1];
if(id[a[i]]) last = max(last, id[a[i]]);
ans += i - last;
id[a[i]] = i + 1;
}
printf("%lld\n", ans);
return 0;
}
D. Challenges in school №41
题意:把右键头抽象成1,左箭头抽象成0。给定一个长度为$n$的01序列,一次操作选择至少一个$a[i]>a[i+1]$的二元组$(i,i+1)$,交换$a[i]$和$a[i+1]$。问能否恰好使用$k$次操作得到一个递增序列,并输出每次操作选择交换的二元组左边的下标。
基本思路:类比冒泡排序去模拟一遍交换的过程,时间复杂度$O(n^2)$。在模拟的过程中记录下每一轮进行交换的位置以及总的轮数$cnt$和总的交换次数$sum$。则必须满足$cnt \leq k \leq sum$才有合法解。
贪心:再来考虑输出的问题。贪心的让前面的操作进行尽可能多的交换,但必须给后面的操作留至少一次交换。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 3e3 + 117;
const int MAXM = 2e5 + 117;
int n, k;
char s[MAXN];
int cnt, sum, a[MAXN];
vector<int> ans[MAXN];
void solve() {
cnt = sum = 0;
while(true) {
bool f = true;
for(int i = 1; i < n; i++) {
if(a[i] > a[i + 1]) {
swap(a[i], a[i + 1]);
ans[cnt].push_back(i);
f = false;
sum++;
i++;
}
}
if(f) break;
cnt++;
}
}
void out() {
int x = 0, y = 0, z = ans[0].size();
while(k--) {
int dy = min(z - y, sum - k);
printf("%d", dy);
sum -= dy;
while(dy--) printf(" %d", ans[x][y++]);
if(y == z) {
x++;
y = 0;
z = ans[x].size();
}
putchar(10);
}
}
int main() {
scanf("%d%d", &n, &k);
scanf("%s", s + 1);
for(int i = 1; i <= n; i++) {
if(s[i] == 'R') a[i] = 1;
else a[i] = 0;
}
solve();
if(cnt <= k && k <= sum) out();
else puts("-1");
return 0;
}
E. Road to 1600
题意:给出国际象棋中车和皇后的走法,给定一个$n$阶的棋盘,每个格子有权值且互不相同,并给出下列约束:
- 初始棋子在权值为1的点,并该点被标记为访问过。
- 如果合法能到的点中有未访问的点,则走到权值最小的点。
- 否则如果还有为访问的点,则走到权值最小的点并且费用+1。
- 否则结束程序。
需要构造一个棋盘使得车的费用严格小于皇后的费用。
基本思路:构造一个棋盘使得车的费用为0而皇后的费用为1。
- $n<3$的时候无解
$n=3$的时候通过搜索或者手动构造基本解,例如下面是一个合法的解:
/* 5 9 1 4 3 2 8 7 6 */
$n>3$的时候,采用递归套娃的思想如果能使$n$阶转化成3阶岂不美滋滋,如下所示只需要区分一下奇数和偶数的情况,剩下的3阶只需要用构造出来的基本解进行转化即可。
/* * * * 7 * * * 6 * * * 5 1 2 3 4 */ /* * * * 1 16 * * * 2 15 * * * 3 14 7 6 5 4 13 8 9 10 11 12 */
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 5e5 + 117;
const int MAXM = 2e5 + 117;
int n;
int s[7][7] = {{5, 9, 1}, {4, 3, 2}, {8, 7, 6}};
int t[517][517];
void left_up(int id, int num);
void down_right(int id, int num) {
for(int i = 0; i <= id; i++)//向下
t[i][id] = num++;
for(int j = id - 1; j >= 0; j--)//向左
t[id][j] = num++;
if(id + 1 < n) left_up(id + 1, num);
}
void left_up(int id, int num) {
for(int j = 0; j <= id; j++)//向右
t[id][j] = num++;
for(int i = id - 1; i >= 0; i--)//向上
t[i][id] = num++;
if(id + 1 < n) down_right(id + 1, num);
}
int main() {
scanf("%d", &n);
if(n < 3) puts("-1");
else {
if(n % 2 == 0) left_up(3, 1);
else down_right(3, 1);
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
if(i < 3 && j < 3) printf("%d ", n * n - 9 + s[i][j]);
else printf("%d ", t[i][j]);
}
putchar(10);
}
}
return 0;
}
F. Kate and imperfection
题意:定义$I_i$是大小为$i$的集合中$gcd(a,b)$的最大值。给定一个全排列的集合,求不同大小的子集的最小$I_i$。
基本思路:贪心的是构造尽量小的$gcd(a,b)$。
- 首先构造$gcd(a,b)=1$的情况,这时候只能把素数加进集合。
- 再构造$gcd(a,b)=2$的情况,可以把4加进集合。
- 再构造$gcd(a,b)=3$的情况,可以把6和9加进集合。
- 观察一下前面的规律,为什么在$gcd(a,b)=2$的时候不能加8以及在$gcd(a,b)=3$的时候不能加12?
- 一个数总可以分解成最小质因子乘以某个数,我们不仅要关心最小质因子是多少,还要关心那个乘数。例如$8=2*4$,虽然8的最小质因子是2,但是乘数是4,$gcd(4,8)=4$使得$I_i$由2增大到4。
- 也就是说一个数x对答案的影响是x/最小质因子,用欧拉筛在$O(n)$的时间复杂度下就能得到每个数对答案的影响,再排序一遍就是最优方案。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 5e5 + 117;
const int MAXM = 2e5 + 117;
int n;
int prime[MAXN], ans[MAXN];
int main() {
scanf("%d", &n);
for(int i = 2; i <= n; i++) {
if(!prime[i]) {
prime[++prime[0]] = i;
ans[i] = 1;
}
for(int j = 1; j <= prime[0] && prime[j] <= n / i; j++) {
prime[prime[j]*i] = 1;
ans[prime[j]*i] = i;
if(i % prime[j] == 0) break;
}
}
sort(ans + 2, ans + n + 1);
for(int i = 2; i < n; i++) printf("%d ", ans[i]);
printf("%d\n", ans[n]);
return 0;
}
版权属于:小影
本文链接:http://kindkidll.com/index.php/archives/134/
所有原创文章采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。 您可以自由的转载和修改,但请务必注明文章来源并且不可用于商业目的。