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

简介: 笔记

A 区间最大值


1.png2.png

#include<iostream>
using namespace std;
int main()
{
    int n, m; cin>> n >> m;
    while(m--) {
      int l, r; cin >> l >> r;
        int ans = 0;
        // [l, r]区间,找到其中分块的每一块左端点,维护最大值
        for (int i = l; i <= r; i = n / (n / i) + 1) {
          ans = max(ans, n % i);
        }
        cout << ans << 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 爬塔


11.png

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
int n, m;
string s;
set<PII> mp[11];
// mp[len] = {{l1, r1}, {l2, r2}, ...}
// 转化问题:通关的最大关卡数 == 最长区间和为0且合法数
void solve() {
    int l, r;
    cin >> l >> r;
    /// 从最长区间开始枚
    for (int i = 10; i >= 1; i--) {
        // 找到>=l的位置
        auto it = mp[i].lower_bound({l, -1});
        if(it == mp[i].end()) continue;
        // 如果左右都在内部,满足能从[l, r]中任意位置开始,走i步区间和位0且合法
        if(it->first >= l && it->second <= r) {
            cout << i << '\n';
            return ;
        }
    }
    cout << 0 << '\n';
}
int main() {
    // n位01, s是01串,m是询问
    cin >> n >> s >> m;
    s = " " + s;
    for (int i = 1; i <= n; i++) {
        int ans = 0;
        for (int j = i; j <= i + 9; j++) {
            if(s[j] == '1') ans++;
            else ans--;
            if(ans < 0) break;
            if(ans == 0) mp[j - i + 1].insert({i, j});
        }
    }
    while(m--) {
        // 每次询问:l,r,问从[l, r]任意位置开始能通关的最大关卡数
        solve();
    }
}

F 一个很大的数


3.png

题解:

4.png

样例:5.png

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串


6.png

题意:


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();}

法二:树状数组

7.png

#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),问最大价值。

8.png

思路:


转化题意:取出K组数,(每组都是偶数个),使其之和最大。


1.外层for k

2.内层从 i 起始步长为k,维护一个最小值 mn(防止最后是奇数,我们需要删一个(选最小的删)),注意这里是每隔一个数维护。(因为不能保证删了一个该组中最小的数,前缀是就偶数个。)

3.sum 加上每组的最大值


9.png10.png

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 一道计数题

相关文章
集美大学第九届程序设计竞赛
集美大学第九届程序设计竞赛
72 0
|
定位技术 Go
第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(银川),签到题5题
第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(银川),签到题5题
97 0
广东工业大学第十四届程序设计竞赛 1004免费送气球(线段树)
广东工业大学第十四届程序设计竞赛 1004免费送气球(线段树)
82 0
|
机器学习/深度学习 Java 定位技术
第 46 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(沈阳),签到题5题
第 46 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(沈阳),签到题5题
303 0
|
机器学习/深度学习 算法
下一篇
DataWorks