目录
A. Red Versus Blue
题目翻译:
输入三个数 n, r, k 别表示 总字符数,R字符数,B字符数,知,r > k ,输出一个字符串,是连续的R字符数量最小;
思路:将 r 分为 (k+1) 个部分,记录 a = r/(k+1) ,b = r%(k+1) 枚举即可,a + b 为一部分,若 b 没了,单独的 a 作为一部分
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6+10, mod = 998244353;
int n, m, k;
void solve()
{
string s;
int n, m, k;
cin >> n >> m >> k;
int t = m/(k+1);
int x = m%(k+1);
//cout << t << endl;
//cout << x << endl;
int res = 0, t2 = 0;
while( res < n)
{
t2 ++;
for(int i = 0; i < t && res < n; i ++ )
printf("R"), res ++;
if(t2 < x) printf("R"), res ++;
if(k > 0) printf("B"), k -- , res ++;
}
puts("");
return ;
}
int main()
{
cin >> n;
while(n -- )
solve();
return 0;
}
B. Bit Flipping
题目翻译:
输入 n, k ;n 表示数组元素个数(数组元素为 0 或 1),k 表示可以反转的此数,每次反转必须选一个数据固定不动;然后,输入n 个数组元素,输出反转后的最大的数据
思路:
从前往后固定元素的值,使每个元素尽量为 1
因为每个元素都得反转,所以我们通过反转的此数的奇偶性和元素的01值,来确定如何使数组元素等于 1,
数组 f 表示固定每个元素的此数
有四种情况:
1) k 为奇数,a[i] 为 1,此时我们要想使 a[i] 为1,要固定一次 a[i],即,f[i] = 1
2)k 为奇数,a[i] 为 0 ,此时我们要想是 a[i] 为 1,不需要固定,即,f[i] = 0
3)k 为偶数,a[i] 为 1 ,此时我们要想是 a[i] 为 1,不需要固定,即,f[i] = 0
1) k 为偶数,a[i] 为 0,此时我们要想使 a[i] 为1,要固定一次 a[i],即,f[i] = 1
最后枚举每个元素的 反转次数 (k-f[i]) 的奇偶性判断是否令元素反转
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef pair<char, int> PCI;
typedef long long LL;
const int N = 2e5+10;
int T;
void solve()
{
int n, k;
scanf("%d %d", &n, &k);
string s;
int a[N], f[N] = {0};
cin >> s;
for(int i = 0; i < n; i ++ )
a[i] = s[i]-'0';
int t = k;
for(int i = 0; i < n-1 && t > 0; i ++ )
{
if( k%2 == a[i])
{
f[i] = 1;
t --;
}
}
f[n-1] = t;
for(int i = 0; i < n; i ++ )
{
if( (k-f[i])%2 ) a[i] = a[i]^1;
}
for(int i = 0; i < n; i ++ )
cout << a[i];
puts("");
for(int i = 0; i < n; i ++ )
cout << f[i] << " ";
puts("");
return;
}
int main()
{
scanf("%d", &T);
while(T -- )
solve();
return 0;
}
C. Line Empire
题目翻译:
背景为国王占领城堡;给定三个数 n, a, b 分别表示,要攻陷的城堡数,搬首都的每公里代价,直接打的每公里代价;
输入 n 个城堡的距离
思路:
贪心,先直接打(因为第一步没法迁都),再判断一下,迁都到该城堡的代价 和 直接打其他城堡的过程中经过此城堡的总代价,取较小的值,
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef pair<char, int> PCI;
typedef long long LL;
const int N = 2e5+10;
int T;
void solve()
{
LL n, a, b, v[N] = {0};
scanf("%lld %lld %lld", &n, &a, &b);
for(int i = 1; i <= n; i ++ )
scanf("%lld", &v[i]);
int tmp = 0;
LL res = 0;
for(int i = 1; i <= n; i ++ )
{
// 不管三七二十一,直接打
res += b*(v[i] - v[tmp]);
// 判断最小代价
if( a*(v[i] - v[tmp]) <= b*(v[i] - v[tmp])*(n-i))
{
res += a*(v[i]-v[tmp]);
tmp = i;
}
}
cout << res << endl;
return;
}
int main()
{
scanf("%d", &T);
while(T -- )
solve();
return 0;
}
D. Reverse Sort Sum
题目翻译:
有一个包含 n 个元素的数组 A ,每个元素都是 0 或 1;
二维数组 B 每行分别为 A 将前 i 个元素升序排序后数组 A,即,B[1] = 升序排序了前 1 个元素的数组A,B[n] 为升序排序了前 n 个元素的数组A;
存在一维数组C,保存的是二维数组 B 的每一列的和;
已知数组C,求数组A
思路:树状数组 + 差分
举例:
红色为已排序部分
我们有题意可知三点线索:
(1):可求得原数组中 1 的个数:
B数组的每一维都是 A 数组的数字一样,只是顺序不一样而已,所以 B 数组的所有元素求和等于 n 行的 A数组求和,求和的结果除以 n 得到,A数组中的 1个数,恰好C数组为B数组的每一列求和,所以C数组的和即为 n个A数组的和,将输入的C数组的值累加除以n即可得到A数组中的1 的个数。
(2):可根据最后一步排序 B[n] 来确定 A 数组的最后一步的数值,B[n] 为A数组全部元素升序排列的结果,若此时 B[1~n-1] 的第 n 个元素都为 1 ,即,无需排序第 n 为元素为 1 (等效于C[n] 等于 n )可得A数组的该位置元素确实为 1 ,若不全都是 1 ,那么可知 A数组该位置的元素为 0
(3):将第二部步骤向前递推,即,根据 B[n-1] 确定 A[n-1] ,B[n-2] 确定 A[n-2] ,以此类推。
具体向前递推过程:
根据(2)计算数组A 的元素的前:1、该操作为最后一步操作;2、确定 C数组对应列的值
将原数组变换为满足条件 1 :
步骤为:将最后一列删去,将最后一行删去,更新其余的 C[i] ,计算即可
更新 C[i] 步骤为,减去 B[ n ](上次计算的一行) 的每一个数 ,我们可知,在B数组中的第 i 行已排序的元素的后 i 个元素必定为 1 (如果 k 有条件的话),具体例子解释:B数组的第四行的后三个元素为 1 ,第三行的二,三两个元素为 1 。
我们更新 C[i] 时仅需上一次计算的行的最后的几步 1 减去 k 个 1 即可(我们令此处计算一列 k 减去 1 ),举例说明,计算 B[3] 时,把 C[2] - 1,C[3] - 1,C[4] - 1(此处减1无意义,但为了理解加上去了,在后续的差分中也会加上去,会方便一点),注意,此处为区间修改操作,可用树状数组简化时间复杂度,达到O(nlogn),减去的区间范围为 [ i - k + 1, i ] ,此数区间长度恰好为 k ,根据B[i] 的升序排列的数据的最大索引 i ,推导出区间左端点为 i - k + 1,(当用树状数组+差分是就直接将 i - k + 1后面数据全部减去 1 ,大于 i 的数据已经没用,不会有任何影响)
区间修改可用树状数组 + 差分实现;
代码如下:
(1)数组版(时间复杂度为O(n^2) TLE 版本,但有助于理解代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef pair<char, int> PCI;
typedef long long LL;
const int N = 2e5+10;
int T;
int n;
int tr[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int c)
{
for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int sum(int x)
{
int res = 0;
for(int i = x; i ; i -= lowbit(i)) res += tr[i];
return res;
}
void solve()
{
memset(tr, 0, sizeof tr);
int a[N], b[N], c[N];
scanf("%d", &n);
LL x = 0;
for(int i = 1; i <= n; i ++ )
{
scanf("%d", &c[i]);
x += c[i];
}
x /= n;
int res[N];
for(int i = n; i ; i -- )
{
int l = i - x + 1;
int t = c[i];
if(t == i) a[i] = 1, x --;
else a[i] = 0;
for(int j = l; j <= n; j ++ )
c[j] --;
}
for(int i = 1; i <= n; i ++ )
printf("%d ", a[i]);
puts("");
return;
}
int main()
{
scanf("%d", &T);
while(T -- )
solve();
return 0;
}
(2)树状数组 + 差分(时间复杂度 O(nlogn) ),AC版本
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef pair<char, int> PCI;
typedef long long LL;
const int N = 2e5+10;
int T;
int n;
int tr[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int c)
{
for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int sum(int x)
{
int res = 0;
for(int i = x; i ; i -= lowbit(i)) res += tr[i];
return res;
}
void solve()
{
memset(tr, 0, sizeof tr);
int a[N], b[N], c[N];
scanf("%d", &n);
LL x = 0;
for(int i = 1; i <= n; i ++ )
{
scanf("%d", &c[i]);
x += c[i];
// 插入数据
add(i, c[i]), add(i+1, -c[i]);
}
x /= n;
int res[N];
for(int i = n; i ; i -- )
{
int l = i - x + 1;
int t = sum(i);
if(t == i) a[i] = 1, x --;
else a[i] = 0;
// 区间修改
add(l, -1);
}
for(int i = 1; i <= n; i ++ )
printf("%d ", a[i]);
puts("");
return;
}
int main()
{
scanf("%d", &T);
while(T -- )
solve();
return 0;
}