A. Little Artem
思路
-
题意:给我们一个nm的方格(2=<n,m<=100),我们可以在这个方格中图成白色或者黑色,现在定义:有效白色方格:方格被涂为白色,并且这个这个方格的四周至少有一个黑色方格;定义:有效黑色方格:方格为涂为黑色,在它的四周至少有个一个白色方格,现在我们要将这nm个方格涂成:有效黑方格的数量 = 有效白方格的数量 + 1,有多个方案输出任意一个
-
思路:
在这一题卡了好久,当时还是没搞懂题意,其实理顺就好了,首先我们考虑 n*m的方格的总数
- 如果是奇数的,我们就直接 黑白间隔涂这样所有的方格被涂色之后都是有效方格且黑方格恰好比白方格多1。
- 如果是偶数,如果我们将一半格子涂成白色的,另一半涂成黑色,假设它们都是有效方格,假设我们将其中一个白方格改成黑方格,这样黑方格总数比白方格数多2,那么我们就要一个黑色方格失效,怎么让他失效,简单就是把一个白的方格变成黑色方格的时候,把一个黑方格挤到墙角那么在墙角的方格失效了,就符合题意了,对于构造我们先令 奇数列都为B,偶数列都为W,那么我们只需改一个固定位置(1, 2)的那个位置变成B,,,,,,,,,,代码的那个思路与这个思路相比不是最优的
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
#include<vector>
#include<queue>
using namespace std;
void fre() { freopen("A.txt","r",stdin); freopen("Ans.txt","w",stdout); }
#define ll long long
#define INF 0x3f3f3f3f
const int mxn = 3e5;
char ar[205][205];
int main()
{
/* fre(); */
int t;
scanf("%d", &t);
while(t --)
{
int m, n;
scanf("%d %d", &m, &n);
int cnt = 1;
if(m%2 > 0 && n%2 > 0)
{
for(int i = 1; i <= m; i ++)
{
for(int j = 1; j <= n; j ++)
{
if(cnt%2)
printf("%c", 'B'), cnt ++;
else
printf("%c", 'W'), cnt ++;
}
printf("\n");
}
}
else if(m%2 || (n%2 == 0 && m%2== 0))
{
for(int i = 1; i <= m; i ++)
{
for(int j = 1; j <= n; j ++)
{
if(j%2)
ar[i][j] = 'B';
else
ar[i][j] = 'W';
}
}
ar[m][2] = 'B';
for(int i = 1; i <= m; i ++)
{
for(int j = 1; j <= n; j ++)
printf("%c", ar[i][j]);
printf("\n");
}
}
else if(n%2)
{
for(int i = 1; i <= m; i ++)
for(int j = 1; j <= n; j ++)
{
if(i%2)
ar[i][j] = 'B';
else
ar[i][j] = 'W';
}
ar[2][1] = 'B';
for(int i = 1; i <= m; i ++)
{
for(int j = 1; j <= n; j ++)
printf("%c", ar[i][j]);
printf("\n");
}
}
else
{
for(int i = 1; i <= m; i ++)
{
for(int j = 1; j <= n; j ++)
{
}
}
}
}
}
B. Kind Anton
思路
-
题意:给我们一个长度为n的序列 其元素只由 组成,有给我们了一个长度为n的序列 ( ),现在我们可以对序列a进行一些操作,对于任意一次操作我们可以选择在序列 中选择两个坐标 ,之后把 的值加到 上,我们可以进行任意多这样的操作,问我们能否把a序列变成b序列
-
代码:这题的突破口就在 这个条件,我们只要枚举j的位置,那么i的范围就确定了,对于每一个i的范围我们就可用前缀和维护正、负数的数量,我们考虑一个j位置如果 那么在 范围a就是一定需要至少有一个正数,如果 那么在 范围a就是一定需要至少有一个负数. 这个判断是否有数的过程,就是要用到前缀和维护的结果
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
#include<vector>
#include<queue>
using namespace std;
void fre() { freopen("A.txt","r",stdin); freopen("Ans.txt","w",stdout); }
#define ll long long
#define INF 0x3f3f3f3f
const int mxn = 3e5;
int a[mxn], b[mxn];
int pz[mxn];
int pf[mxn];
int main()
{
/* fre(); */
int t;
scanf("%d", &t);
while(t --)
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
pz[i] = pz[i - 1];
pf[i] = pf[i - 1];
if(a[i] > 0)
pz[i] ++;
else if(a[i] < 0)
pf[i] ++;
}
int fg = 1;
for(int i = 1; i <= n; i ++)
scanf("%d", &b[i]);
int tmp;
for(int i = n; i >= 1; i --)
{
tmp = b[i];
if(tmp - a[i] > 0 && pz[i - 1] == 0)
fg = 0;
else if(tmp - a[i] < 0 && pf[i - 1] == 0)
fg = 0;
}
if(fg)
printf("YES\n");
else
printf("NO\n");
}
}
C. Eugene and an array
思路
- 题意
- 定义 子数组:给我们一个数组,我们从数组的前边删除多个或0个元素,在从数组的后边删除多个或0个元素,剩下的就是子数组。
- 定义 好数组:这个数组任何一个子数组的元素和都不为零,那么那么这个数数组就叫好数组。
- 本题给我们n个元素的数组a[],让我们统计这个a这个数组中的所有的子数组中有多少个是好数组??
- 分析:明显的一个区间计数问题,其中的思想很值得我们学习,下面是思路过程
- 首先我们从1~n枚举i,以i作为区间右边界,我们现在讨论[1, i]这个区间中以
i为结尾的子数组
(例如: ),那么我们现在假设在[1,i]区间中有一个这样的子区间[now, x]它的元素和相加为0,那么我们靠虑的以i为结尾的子数组
,就不应该包含 完全包含这个子区间,那么现在我们要讨论的是区间[now+1,i]中以i结尾的好子数组的数量即为:i - now
,这样就我们就得到了[1,i]中好数组的数量了 - 从上面我们枚举的过程,应该能发现找[now,x]这个子区间是最难的(更准确的说是
维护区间左端点now
),这个时候就需要个一个好的维护方法 ,其实我我们 可以用一个 放法来记录前缀维和+vector来维护前后两个相同的状态相同的数,例如一个子序列 我们假设如果x为的前缀和为10,这个时候正好y位置的前缀和也是10,那么 之间的元素和就一定为0,那么我们通过第一开始遍历到x位置,记录到x的位置s1,而到y位置的时候发现了有发现了与y位置相同的状态,通过相同的状态我们可以调出x的位置s1,那么就知道now = s1+1,,,,,大概就是这个过程了
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
#include<vector>
#include<queue>
#include<string>
using namespace std;
void fre() { freopen("A.txt","r",stdin); freopen("Ans.txt","w",stdout); }
#define ll long long
#define INF 0x3f3f3f3f
const int mxn = 3000005;
int ar[mxn];
map<ll, ll> vis;
int main()
{
/* fre(); */
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d", &ar[i]);
ll ans = 0, sum = 0, now = 0;
vis[0] = 0;
for(int i = 1; i <= n; i ++)
{
sum += ar[i];
if(vis.count(sum) == 1)
now = max(now, vis[sum] + 1); //vis[sum] + 1 就是一个区间的左端点,这个区间[vis[sum]+1, i] 中的元素的和为0
//[now, i] 指的是 距离当前右边界i最近的区间和为0的子区间,那么以i为有边界的子区间[x, i]不能包含这个区间[now, i] 即:当x > now, 所有符合题意的区间为:[x,i]
vis[sum] = i;
ans += i - now; //我们用i - now 就是得到的方案数,而每个方案的区间是:[now+1, i]、[now+2, i]、[now+3, i]...[i, i];
}
printf("%lld\n", ans);
return 0;
}
D. Challenges in school №41
思路
-
题意:开始n个人站成一行有的头朝向右边,有个字符‘R’表示,有的头朝向左边用‘L’表示,所以为了描述他们朝向情况给了我们一个长度为n的由‘L’、‘R’组成的字符串s,对于每一次操作 我们可以令
至少一对相邻
的两个脸相对的人都各自调转头的方向,问我们能否正好经过k次操作使这个n个人中相邻的人没有脸对脸的情况,如果能输出k次操作中每次改变头朝向的一对人左边的那个人的下标? -
分析
- 这一题我们看懂 “两个脸相对的人改变朝向”,这个操作在字符串s中的本质是内容什么?,,,其实就是相邻的两个字符「R,L」交换位置变成「L,R」这个交换过程。
- 有了上面的理解,我们考虑怎么样才可判读?确定?k次操作恰好能够变成没有脸相对的情况,这个问题我们只找到 变成没有脸相对的情所需要的操作的上下边界就可以
- 对于操作的下边边界l,那么就是对于每次操作我们让所有脸相对的都改变头的方向,这样就可统计出最少的操作次数,注意这个操作的时候要存储每一次操作 改变头朝向左边的那个人的下标
- 对于上边界r,那么对于每次操作,我们只让 一对脸相对的人改变头的朝向
- 如果 就是一定有答案,可能会出现 k > l 的情况,这个时候只需要 在求下边界时存储的某次操作中如果改变成了多个人头朝向,拆分成多次操作,就好了,举例子例如某次操作我们一下改变了下表「2,4,7」这个三个人头的头朝向,那么我们可分成三次操作分别改「2」、「4」、「7」这个三个人的头朝向,这个时候操作次数从1变成了3次增加了2次,,,,过程大致就是这个过程里了,具体看代码吧
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
#include<vector>
#include<queue>
#include<string>
using namespace std;
void fre() { freopen("A.txt","r",stdin); freopen("Ans.txt","w",stdout); }
#define ll long long
#define INF 0x3f3f3f3f
const int mxn = 30005;
vector<int> vec[mxn];
int main()
{
/* fre(); */
int n, k;
cin >> n >> k;
string s;
cin >> s;
s = '0' + s;
int cnt = 0;
ll sum = 0;
while(true)
{
int fg = 1;
cnt ++;
for(int i = 1; i < n; i ++)
{
if(s[i] == 'R' && s[i + 1] == 'L')
{
vec[cnt].push_back(i);
swap(s[i], s[i + 1]);
i ++;
fg = 0;
}
}
if(fg)
break;
sum += vec[cnt].size();
}
if(vec[cnt].size() == 0)
cnt --;
if(k < cnt || k > sum)
{
puts("-1\n");
return 0;
}
for(int i = 1; i <= cnt; i ++)
{
int p = 0, len = vec[i].size();
while(p < len && k > cnt - i + 1)
{
printf("1 %d\n", vec[i][p]);
p ++;
k --;
}
if(p < len)
{
printf("%d ",len-p);
for(int j=p;j<len;++j)
printf("%d%c",vec[i][j],j==len-1?'\n':' ');
k--;
}
}
return 0;
}
F. Kate and imperfection(思路)
思路
-
题意:给我我们一个长度为n的集合S其元素为 ,先在定义“不完美值”,设集合 ,则M的不完美值是,M中任意两个元素 的最大公约数的最大值,现在让我们分别求长度k为 时S的所有子集合中 所能产生的最小 “不完美值” 是多少
-
分析:我们考虑一个长度为 k的子结合M,很明显M中的数都是“互为质数”的关系的话,那就好了,这个时候的最大公因数肯定是 1了,但是情况并不都是这样,我们现在假设S中所有“互为质数”的元素个数为 cnt 的话
- 如果k <= cnt,明显我们能让M中所有的元素关系均为“互质”的关系,此时最大公因数就是1
- 如果k > cnt的话,我们不能让所有的元素都为“互转”的关系,我们先把S总cnt个互为质数的数 放入到M集合中去,这个M中时候还缺 k - cnt 个数,这个时候我们在S中剩余的元素选择时,这个时候M的“完美值”一定是大于1的,我们考虑如果“完美值”是2的时候我们应该选择那些数?,考虑一下为们因该选择那些数值为M中所有质数元素中的一个成二倍关系的数,如果“完美值”为3的话,我们就选择,与M中所有质数元素中的一个成三倍关系的数,,,这样一次类推,考虑“完美值”为 4、5、5、,,,,时的选择
- 最后实现很简单,就是 埃氏筛 依次找 某个 质数的 1,2,3…倍,,,最后sort一下输出就行了,,具体看代码
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
#include<vector>
#include<queue>
#include<string>
using namespace std;
void fre() { freopen("A.txt","r",stdin); freopen("Ans.txt","w",stdout); }
#define ll long long
#define INF 0x3f3f3f3f
const int mxn = 30005;
vector<int> mx_div;
void Shai(int n)
{
mx_div.assign(n + 1, 0);
mx_div[0] = INF;
mx_div[1] = 1;
for(int i = 2; i <= n; i ++)
{
if(mx_div[i]) continue;
for(int j = i; j <= n; j += i)
{
if(mx_div[j]) continue;
mx_div[j] = j/i;
}
}
}
int main()
{
/* fre(); */
int n;
cin >> n;
Shai(n);
sort(mx_div.begin(), mx_div.end());
for(int i = 1; i < n; i ++)
cout << mx_div[i] << ' ';
return 0;
}