好菜啊,F变量名写错了没能AK,哭了。
题意:给一个n和m,构造出一个长度为n的非负序列a,a中元素之和为m,要求a相邻两项差的和最大,输出这个最大值。
数据范围:1 <= n, m <= 1e9
题解:显然是直接放一个m就行了,判断一下长度就可以了。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
scanf("%d", &t);
while(t--) {
int n, m;
scanf("%d%d", &n, &m);
if(n == 1) m = 0;
else if(n > 2) m <<= 1;
printf("%d\n", m);
}
return 0;
}
题意:给两个长度为n的序列a和b,每次操作可以交换a和b中的一个元素(任意交换),问至多进行k次操作,序列a的元素之和最大是多少。
数据范围:1 <= n <= 30,1 <= ai, bi <= 30, 0 <= k <= n
题解:直接对a,b进行排序,将b中大的和a中小的换即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 7;
int a[maxn], b[maxn];
int main() {
int t;
scanf("%d", &t);
while(t--) {
int n, k;
scanf("%d%d", &n, &k);
int ans = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
ans += a[i];
}
for(int i = 1; i <= n; ++i) {
scanf("%d", &b[i]);
}
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1);
int r = n;
for(int i = 1; i <= n; ++i) {
if(k == 0) break;
if(b[r] > a[i]) {
ans += b[r] - a[i];
--k;
--r;
}
}
printf("%d\n", ans);
}
return 0;
}
题意:给一个n * n的矩阵(n为奇数),矩阵中每个位置有一个人,每次操作可以使一个人向8个方向移动一格,问将所有人移动到同一格的最少操作数。
数据范围:1 <= n <= 5e5
题解:显示将所有人移到最中间操作数最少,8方向移动两个位置最短距离即max(|x1 - x2|, |y1 - y2|),假设矩阵中点位置为(mid, mid),则所求答案为:
发现有绝对值不好算,将矩阵分成四块,四块的答案显然是一样的,算其中一块即可。具体计算很简单。
小插曲:比赛时,直接算了没加绝对值的式子,算出了发现答案少了一倍,于是*2就交了上去,比赛下来以为要FST,仔细想了想,发现是对的。当时真莽啊。
发现这东西其实可以O(1)计算,不过没什么必要。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
int t, n;
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
int mid = (n + 1) / 2;
ll ans = - 1LL * n * n * mid;
ans += 1LL * n * n * (1 + n) / 2;
// for(int i = 1; i <= n; ++i) {
// ans += 1LL * (n - i) * i;
// }
--n;
ans += 1LL * (n + 1) * n * (n + 1) / 2;
ans -= 1LL * n * (n + 1) * (2 * n + 1) / 6;
printf("%I64d\n", ans << 1);
}
return 0;
}
题意:给一个正整数n,让你构造一个长度为n的序列a,构造方法如下:
初始a中元素全为0。
每次操作,选最长的全0子段,如果有多个选最左边的那个,设这个区间为[l, r],如果区间长度为奇数,则使a[(l + r) / 2] = i,
否则使a[(l + r - 1) / 2] = i。其中i表示当前是第i次操作。
数据范围:1 <= n <= 2e5
题解:将区间放入优先队列,按照题目要求排序,每次取堆顶赋值, 再分成两半,直到没有合法区间。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 7;
int a[maxn], n;
struct node{
int l, r;
bool operator < (const node &oth) const {
int len1 = r - l + 1, len2 = oth.r - oth.l + 1;
if(len1 == len2) return l > oth.l;
return len1 < len2;
}
};
priority_queue<node> pq;
int main(){
int t;
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
pq.push(node{1, n});
int idx = 1;
while(!pq.empty()) {
node p = pq.top(); pq.pop();
int mid = (p.l + p.r) / 2;
a[mid] = idx;
++idx;
if(mid - 1 >= p.l) pq.push(node{p.l, mid - 1});
if(mid + 1 <= p.r) pq.push(node{mid + 1, p.r});
}
for(int i = 1; i <= n; ++i) printf("%d%c", a[i], i == n ? '\n' : ' ');
}
return 0;
}
题意:给一个长度为n的01串。
定义一次操作为选一个位置将此位置的值反转。
定义k-periodic串:串中相邻1之间有k-1个0。
问最少几次操作可以将给定串变成k-periodic串。
数据范围:1 <= n <= 1e6, 1 <= k <= n
题解:由题意可知,串中模k同余的位置互相影响,并且与其他位置互不影响,我们将模k同余的位置取出来分别做,这样问题转化为给一个串,操作还是题目中的操作,问最少几次操作能使串中的1都连续。这个问题可以通过dp解决,枚举当前端点为1的最后端点,显然,要么前面全变成0,要么当前位的前一位变成1,对这两个取最小值即可。后面全变成0的代价可以预处理一下。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 7;
char s[maxn];
int n, k;
string sk[maxn];
int main() {
int t;
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &k);
scanf("%s", s);
for(int i = 0; i < k; ++i) sk[i].clear();
int one = 0;
for(int i = 0; i < n; ++i) {
sk[i % k] += s[i];
if(s[i] == '1') ++one;
}
int ans = n + 1;
for(int i = 0; i < k; ++i) {
int tans = one;
int tone = 0;
for(char c : sk[i]) if(c == '1') ++tone;
tans -= tone;
int ttone = 0;
int res = n + 1, dp = n + 1;
for(char c : sk[i]) {
if(c == '1') {
dp = min(dp, ttone);
++ttone;
}
else {
dp = min(dp + 1, ttone);
}
res = min(res, dp + tone - ttone);
//printf("i -- %d res -- %d dp -- %d\n", i, res, dp);
}
//printf("i -- %d tans -- %d\n", i, tans);
tans += res;
ans = min(ans, tans);
}
printf("%d\n", ans);
}
return 0;
}
题意:给一个n*m的矩阵a,要从(1, 1)走到(n, m),每次只能向下(i => i + 1)或向右(j => j + 1)走,要求路径上相邻点的权值差1且递增。现在有一种操作,一次操作可以选出矩阵中的一个位置(i, j)使a[i][j] -= 1。问最少几次操作可以使(1, 1)能够走到(n, m)。
数据范围:1 <= n, m <= 100, 1 <= aij <= 1e15
题解:数据范围比较小,我们可以枚举路径上的经过点(x, y),使其值不变,其他值跟随其变化,做两个dp,一个从(x, y) 到(n, m)的dp和一个从(x, y)到(1, 1)的dp。枚举后取最小值。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100 + 7;
const ll inf = 1e18;
ll a[maxn][maxn], dp[maxn][maxn], dp2[maxn][maxn];
int n, m;
ll solve(int x, int y) {
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) dp[i][j] = dp2[i][j] = inf;
}
dp[x][y] = 0;
for(int i = x; i <= n; ++i) {
for(int j = y; j <= m; ++j) {
if(i == x && j == y) continue;
ll nowval = a[x][y] + i - x + j - y;
if(nowval > a[i][j]) continue;
ll cost = a[i][j] - nowval;
if(i - 1 >= x) {
dp[i][j] = min(dp[i][j], dp[i - 1][j] + cost);
}
if(j - 1 >= y) {
dp[i][j] = min(dp[i][j], dp[i][j - 1] + cost);
}
}
}
dp2[x][y] = 0;
for(int i = x; i >= 1; --i) {
for(int j = y; j >= 1; --j) {
if(i == x && j == y) continue;
ll nowval = a[x][y] - (x - i + y - j);
if(nowval > a[i][j]) continue;
ll cost = a[i][j] - nowval;
if(i + 1 <= x) {
dp2[i][j] = min(dp2[i][j], dp2[i + 1][j] + cost);
}
if(j + 1 <= y) {
dp2[i][j] = min(dp2[i][j], dp2[i][j + 1] + cost);
}
}
}
return dp[n][m] + dp2[1][1];
}
int main() {
int t;
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
scanf("%I64d", &a[i][j]);
}
}
ll ans = inf;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
ans = min(ans, solve(i, j));
}
}
printf("%I64d\n", ans);
}
return 0;
}