0
点赞
收藏
分享

微信扫一扫

文远知行杯广东工业大学第十六届程序设计竞赛

一葉_code 2022-03-30 阅读 88
算法c++

文远知行杯广东工业大学第十六届程序设计竞赛

文章目录

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串

在这里插入图片描述

题意:

  1. op == 1:询问 [l, r] 区间修改为全0或1的最小操作次数
  2. op == 2:对 [l, r] 区间进行按位取反
  3. (修改:对字符串前缀或后缀按位取反)

思路:
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组数,(每组都是偶数个),使其之和最大。

  1. 外层for k
  2. 内层从 i 起始步长为k,维护一个最小值 mn(防止最后是奇数,我们需要删一个(选最小的删)),注意这里是每隔一个数维护。(因为不能保证删了一个该组中最小的数,前缀是就偶数个。)
  3. 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 << ' ';
}

J 一道计数题


举报

相关推荐

0 条评论