文远知行杯广东工业大学第十六届程序设计竞赛
文章目录
A 区间最大值
思路:见注释
int n, m;
void solve() {
cin >> n >> m;
int l, r, i;
while(m--) {
cin >> l >> r;
// 相当于是找到了下一个阶级分块处,r到此用处结束
for (i = 2; r <= n / i; i++);
// 看l和该级分块左端大小
// 小:则取当前分块左端那个值(最大),因为是递减
if(l < n / i + 1) cout << n % (n / i + 1) << endl;
// 大:则取l处
else cout << n % l << endl;
}
}
B 模块改造
#include<stdio.h>
int main(){
int s,t,i;
char sr[50]={0};
scanf("%s",sr+1);
s=0;t=0;
for(i=1;i<50;++i)
{
if(sr[i]=='1')t=i;
else
{
if(sr[i-1]=='1')
printf("%02d:%02d - %02d:%02d\n",s/2,s%2*30,t/2,t%2*30);
s=i;
}
}
if(t==0)printf("00:00 - 00:00");
return 0;
}
C 矩阵变换
D 猪猪也想C位出道
E 爬塔
F 一个很大的数
题解:
样例:
void solve() {
int n; cin >> n;
int x, k;
int res = 1;
while(n--) {
cin >> x >> k;
res = (res * (2 * k + 1)) % mod;
}
cout << res << endl;
}
G 猪猪补习班
H 变换01串
题意:
- op == 1:询问 [l, r] 区间修改为全0或1的最小操作次数
- op == 2:对 [l, r] 区间进行按位取反
- (修改:对字符串前缀或后缀按位取反)
思路:
3. 盲猜结论:前缀后缀改0改1 等价于 前缀全改0
4. 由此:010010:发现其实对前缀有1的地方修改(0会要在改一次变回),那么操作1的答案:区间段数 - 1
5. 线段树维护:
1. tr[]:区间段数
2. LL[]:区间左端点是0还是1
3. RR[]:区间右端点是0还是1
4. add[]:懒标记
5. 段数 = 两端交界处相同则需要 -1
#include<bits/stdc++.h>
const int N = 100010;
using namespace std;
#define ls u<<1
#define rs u<<1|1
int n, m;
string s;
int tr[N << 2], L[N << 2], R[N << 2], add[N << 2];
// LL, RR 维护当前区间左右端点的01信息
int LL[N << 2], RR[N << 2];
void pushup(int u) {
tr[u] = tr[ls] + tr[rs];
if(RR[ls] != LL[rs]) tr[u]++;
LL[u] = LL[ls];
RR[u] = RR[rs];
}
void pushdown(int u) {
if(add[u]) {
LL[ls] ^= 1, RR[ls] ^= 1, add[ls] ^= 1;
LL[rs] ^= 1, RR[rs] ^= 1, add[rs] ^= 1;
add[u] ^= 1;
}
}
void build(int u, int l, int r) {
L[u] = l, R[u] = r;
if(l == r) {
tr[u] = 0; // 你想想:询问 [2, 2] 是不是不用操作,就是0步(一段 - 1)
if(s[l] == '0') LL[u] = RR[u] = 0;
else LL[u] = RR[u] = 1;
return ;
}
int mid = l + r >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r) {
int ll = L[u], rr = R[u];
if(ll >= l && rr <= r) {
LL[u] ^= 1, RR[u] ^= 1, add[u] ^= 1;
return ;
}
pushdown(u);
int mid = ll + rr >> 1;
if(l <= mid) modify(ls, l, r);
if(r > mid) modify(rs, l, r);
pushup(u);
}
// [0, 1, 2] = [段数,左端点,右端点]
vector<int> query(int u, int l, int r) {
vector<int> now;
vector<int> ans = {0, -1, -1};
int ll = L[u], rr = R[u];
if(ll >= l && rr <= r) {
return vector<int>({tr[u], LL[u], RR[u]});
}
pushdown(u);
int res = 0;
int mid = ll + rr >> 1;
if(l <= mid) {
now = query(ls, l, r);
ans[0] += now[0];
if(ans[2] == -1) ans[2] = now[2];
ans[1] = now[1];
}
if(r > mid) {
now = query(rs, l, r);
ans[0] += now[0];
// 上一个query执行,两段交界处不等,段数++
if(ans[2] != -1 && ans[2] != now[1]) ans[0]++;
ans[2] = now[2];
if(ans[1] == -1) ans[1] = now[1];
// 如果上面一个query没执行,ans还是初始值
}
return ans;
}
void solve() {
cin >> n >> m;
cin >> s; s = '@' + s;
build(1, 1, n);
while(m--) {
int op, l, r;
cin >> op >> l >> r;
if(op == 1) {
cout << query(1, l, r)[0] << endl;
} else {
modify(1, l, r);
}
}
}
signed main() {solve();}
#include <iostream>
using namespace std;
const int N = 100010;
int tr[N], n, m;
string s;
int lowbit(int x) { return x & -x; }
int sum(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void add(int x, int c) {
for (int i = x; i <= N; i += lowbit(i)) tr[i] += c;
}
int main() {
cin >> n >> m;
cin >> s; s = '@' + s;
for (int i = 2; i <= n; i++) if(s[i] != s[i - 1]) add(i, 1);
while(m--) {
int op, l, r; cin >> op >> l >> r;
if(op == 1) {
int res = sum(r) - sum(l);
cout << res << endl;
} else {
if(l > 1) {
if(sum(l) - sum(l - 1)) add(l, -1);
else add(l, 1);
}
if(r < n) {
if(sum(r + 1) - sum(r)) add(r + 1, -1);
else add(r + 1, 1);
}
}
}
}
I V字钩爪
题意: 任意取多次,每次取两个(间隔为k),问最大价值。
思路:
转化题意:取出K组数,(每组都是偶数个),使其之和最大。
- 外层for k
- 内层从 i 起始步长为k,维护一个最小值 mn(防止最后是奇数,我们需要删一个(选最小的删)),注意这里是每隔一个数维护。(因为不能保证删了一个该组中最小的数,前缀是就偶数个。)
- sum 加上每组的最大值
int a[N];
void solve() {
int n, k; cin >> n >> k;
int sum = 0;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= k; i++) {
int cnt = 0, mn = 1e17;
for (int j = i; j <= n; j += k) {
cnt ++ ;
if(cnt & 1) mn = min(mn, a[j]);
sum += a[j];
}
if(cnt & 1) sum -= mn;
}
cout << sum << ' ';
}