CF - 1332 A-D
A. Exercising Walk
题意:
- 小猫初始位置在 ( x , y ) (x,y) (x,y),要求小猫左右下上各走 a 、 b 、 c 、 d a、b、c、d a、b、c、d步。限制小猫咪必须在左下角为 ( x 1 , y 1 ) (x_1, y_1) (x1,y1) 右上角为 ( x 2 , y 2 ) (x_2, y_2) (x2,y2) 的区域内。问小猫咪是否能够完成任务?
思路:
- 首先应该判断小猫咪是否可以移动:左右任意一个方向要走的步数不为0,并且区域的宽度为0,则说明左右不可移动;同样地,上下任意一个方向要走的步数不为0,并且区域的高度为0,则说明上下不可移动。这两种情况下是一定不能完成任务的。
- 其他情况,那我们很容易可以知道,只要可以移动我们就可以一左一右,将左右方向步数较少的那个给消耗完;一上一下,将上下方向步数较少的那个给消耗完,然后依然保持在原地不动。
- 接下来我们就需要判断剩下的朝向每个方向的步数是不是能够在合法区域内完成,即可!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = -f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 100005;
int main()
{
int t; t = read();
while(t -- )
{
int l, r, d, u;
l = read(); r = read(); d = read(); u = read();
int x, y, x1, y1, x2, y2;
x = read(); y = read(); x1 = read(); y1 = read(); x2 = read(); y2 = read();
if(x2 - x1 == 0 && (l || r) || y2 - y1 == 0 && (d || u))
{
puts("NO");
continue;
}
if(l < r) r -= l, l = 0;
else l -= r, r = 0;
if(d < u) u -= d, d = 0;
else d -= u, u = 0;
if(x - l >= x1 && x + r <= x2 && y + u <= y2 && y - d >= y1)
puts("YES");
else
puts("NO");
}
return 0;
}
B. Composite Coloring
题意:
- 给出一个合数数列,要给这个数列的每个数染色。必须保证相同颜色的两个数之间,最大公因数>1,也就是这两个数不互质。隐含题意:就算两个数不互质也可以是不同颜色。输出一组染色情况。【这里的颜色只给了11种】
思路:
- 我们知道任何一个合数都可以唯一拆分成多个质数相乘的形式,并且合数 n n n 最小的质因子不会超过 n \sqrt{n} n 。
- 我们先打表看下1000之内的质数,一共只有168个。发现第11个质数31,它的平方31*31是小于1000的最大的值。
- 因为序列中每个数都是1000之内的合数,所以说我们可以肯定这些数的最小质因子一定都是在前11个质因子里面。也就保证了颜色不会超过11.
- 我们如何给每个数染色呢?
- 我们定义数组color[ ]表示对应11个质数的颜色序号,定义tot表示当前颜色的数量。
- 遍历整个序列,对于每个合数遍历前11个质数,找到它的最小的质数因子,染成质数因子对应的颜色。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = -f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 1003;
bool vis[maxN];
int prime[maxN], cnt;
void init()
{
cnt = 0;
vis[0] = true;
vis[1] = true;
}
void PRIme()
{
init();
for(int i = 2; i <= 1000; ++ i )
{
if(!vis[i])
prime[++cnt] = i;
for(int j = 1; j <= cnt; ++ j )
{
if(i * prime[j] > 1000) break;
vis[i * prime[j]] = true;
if(i % prime[j] == 0) break;
}
}
}
int ans[maxN];
int main()
{
PRIme();
int color[15], tot;
int t = read();
while(t -- )
{
memset(color, 0, sizeof(color));
tot = 0;
int n, a[maxN];
n = read();
for(int i = 1; i <= n; ++ i)
a[i] = read();
for(int i = 1; i <= n; ++ i )
{
for(int j = 1; j <= 11; ++ j )
{
if(a[i] % prime[j] == 0)
{
if(!color[j])
color[j] = ++ tot;
ans[i] = color[j];
break;
}
}
}
printf("%d\n", tot);
for(int i = 1; i <= n; ++ i )
printf("%d ", ans[i]);
putchar('\n');
}
return 0;
}
C. K-Complete Word
题意:
- 要将一个长度为 n n n 的字符串转换成有 n k \displaystyle\frac{n}{k} kn 个长度为 k k k 的相同子串的回文串,也就是有循环周期的回文串。问最少需要改动多少个字符?
思路:
- 如果是有循环周期的回文串的话,那么一定每个循环节都是回文串。那么我们只要找到应该相同的字符,然后取字符最多的那个即可。
举个栗子
所谓的字符序列一共有多少呢?
答:
i
=
k
−
1
−
i
i=k-1-i
i=k−1−i 得到
i
=
(
k
−
1
)
/
2
i=(k-1)/2
i=(k−1)/2,所以一共分为了
(
k
−
1
)
/
2
(k-1)/2
(k−1)/2 个字符序列。找到每个序列中出现次数最多的那个字符作为目标字符,将序列中所有的字符都改为该目标字符。
时间复杂度?
(
k
−
1
)
/
2
(k-1)/2
(k−1)/2 个字符序列,每个序列中最多有
2
n
k
\displaystyle\frac{2n}{k}
k2n 个字符。枚举每个序列中的每个字符,其实也就是原字符串字符个数
O
(
n
)
O(n)
O(n)
(呜呜呜,体验了一把赛后一分钟过题的滋味QAQ
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = -f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 200005;
int n, k, num;
char s[maxN];
int cunt[26];
vector<char>vt[maxN];
int main()
{
int t; t = read();
while(t -- )
{
n = read(); k = read(); num = n / k;
cin.getline(s, sizeof(s));
for(int i = 0; i <= (k - 1) >> 1; ++ i ) vt[i].clear();
for(int i = 0; i <= (k - 1) >> 1; ++ i )
{
for(int j = 0; j < num; ++ j )
{
if(i + k * j == (j + 1) * k - 1 - i)
vt[i].push_back(s[i + k * j]);
else
vt[i].push_back(s[i + k * j]), vt[i].push_back(s[(j + 1) * k - 1 - i]);
}
}
int ans = 0;
for(int i = 0; i <= (k - 1) >> 1; ++ i )
{
int sz = vt[i].size(), tmp = 0;
memset(cunt, 0, sizeof(cunt));
for(int j = 0; j < sz; ++ j )
++cunt[vt[i][j] - 'a'];
for(int j = 0; j < 26; ++ j )
tmp = max(tmp, cunt[j]);
ans += (sz - tmp);
}
printf("%d\n", ans);
}
return 0;
}
D. Walk on Matrix 【构造矩阵】
题意:
一个 n ∗ m n*m n∗m 的矩阵,在矩阵中只能向下向右走。问从 ( 1 , 1 ) (1,1) (1,1) 走到 ( n , m ) (n,m) (n,m), 找到一条路径,路径上所有元素值按位与,使得按位与的结果是所有路径中最大的。
Bob写了一个假 d p dp dp, d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] & a [ i ] [ j ] , d p [ i ] [ j − 1 ] & a [ i ] [ j ] ) dp[i][j]=max(dp[i-1][j]\&a[i][j], dp[i][j-1]\& a[i][j]) dp[i][j]=max(dp[i−1][j]&a[i][j],dp[i][j−1]&a[i][j])。显然是错的,错就错在 d p [ i ] [ j ] dp[i][j] dp[i][j] 每次都是当前为止最优,但是后面再按位与的时候,可能需要前面次优的答案。
现在要我们构造出一个矩阵,使得真正的答案和Bob的算法得出的答案相差 k k k.
思路:
我们可以选择将Bob的答案强制搞成0,那么真正的答案搞成k,则满足题意。如何构造这样的矩阵呢?
如果想要最后得到 k k k,有一个方式就是 2 p + k & k 2^p+k \ \& \ k 2p+k & k. 按照Bob的方法得到0,那么可以 2 p + k & 2 p & k 2^p+k \ \& \ 2^p \ \& \ k 2p+k & 2p & k
所以我们可以构造出这样的矩阵
2
p
+
k
2
p
0
k
2
p
+
k
k
\begin{matrix} 2^p+k & 2^p & 0\\ k & 2^p+k & k\\ \end{matrix}
2p+kk2p2p+k0k
正确路径:答案
k
k
k
Bob路径:答案
0
0
0
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = -f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 1003;
int main()
{
int k;
while(cin >> k)
{
int p = ceil(log2(k));
if(1 << p == k) ++p;
cout << "2 3\n";
cout << (1 << p) + k << ' ' << (1 << p) << ' ' << '0' << endl;
cout << k << ' ' << (1 << p) + k << ' ' << k << endl;
}
return 0;
}