A
打表,发现是这样的东西:
然后规律很显然,相邻的两个数,一组在左边,另一组在右边,依次循环,偶数的时候是 \(23\) 开头,奇数的时候是 \(12\) 开头,再处理一下 \(1\) 和 \(n\) 就可以,比较简单的分讨
显然规律不止一个
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int a[1000001];
int main()
{int t;cin >> t;while(t--){int n;cin >> n;if(n == 1){cout << 1 << endl;continue;}else if(n == 2){cout << "1 2" << endl;continue;}if(n % 2 == 1){int mid = (n + 1) / 2;a[1] = n - 1;a[n] = n;a[mid] = 1;for(int i = 2; i < mid; i++){if(i % 2 == 0)a[i] = a[i - 1] - 1;elsea[i] = a[i - 1] - 3;}for(int i = n - 1; i > mid; i--){if(i % 2 == 0)a[i] = a[i + 1] - 3;elsea[i] = a[i + 1] - 1;}}else{int mid;if(n % 4 == 0)mid = n / 2 + 1;elsemid = n / 2;a[1] = n - 1;a[n] = n;a[mid] = 1;for(int i = 2; i < mid; i++){if(i % 2 == 0)a[i] = a[i - 1] - 1;elsea[i] = a[i - 1] - 3;}for(int i = n - 1; i > mid; i--){if(i % 2 == 1)a[i] = a[i + 1] - 3;elsea[i] = a[i + 1] - 1;}}for(int i = 1; i <= n; i++)cout<<a[i]<<' ';cout<<endl;}
}
官解:
B
比较典的线段树,题目核心是找最小缺失编号、加/减球、移除最少的球使 \(x\) 是偶数,你可以用两个线段树
-
一个主线段树(ST_all),作用:维护编号 \(1\) 到 \(n\) 处的球数,支持区间更新,查询最小缺失编号 \(x\)
-
偶数编号线段树(st_even):只储存偶数编号的球数,和找到最少需要移除的球数,使得缺失的编号为偶数
具体实现:用 ST_all
来二分查询出第一个个数为 \(0\) 的下标 \(j\),没有的话就令 \(j = n+1\);用 ST_even
维护偶数位置,对于一个偶数 \(i\),如果球数是 \(b_i\),那么如果使用魔法取出所有 \(i\) 号球,其代价为 \(b_i\),更新时,由于更新是按原下标区间给出的,我们只对落在 \([l,r]\) 中且下标为偶数的数字进行更新。
判断胜负与代价时,假设现在缺的是 \(j\):
-
\(j\) 是偶数,那么 \(Thephix\) 已经赢了,就是 \(0\)
-
\(j\) 是奇数,让缺失数字变为 \(i\),需要代价是 \(b_i\),显然取成本最低的那个就行,如果区间 \([2,j-1]\) 内没有偶数,就不能获胜,输出 \(-1\),注意如果一开始 \(b_1 = 0\),或第一个缺失数字 \(j=1\),那么无论如何都赢不了,输出 \(-1\)
因为魔法移除只对当前回合起作用,回合结束后恢复原状态,因此我们只在查询时“虚拟地”考虑移除操作,不修改主数据结构
复杂度 \(O(m \log n)\)
点击查看代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <cassert>
using namespace std;
typedef long long ll;
const ll INF = 1LL << 60;
int a[500001];
// 构造偶数下标数组(下标从1开始)
vector<int> even_idx;
vector<ll> even_a;
// 线段树模板:支持区间加更新和区间最小值查询
struct ST
{int n;vector<ll> tree, lazy;ST(int n) : n(n){tree.assign(4 * n, INF);lazy.assign(4 * n, 0);}void build(int idx, int l, int r){if(l == r){tree[idx] = a[l - 1];return;}int mid = (l + r) / 2;build(idx * 2, l, mid);build(idx * 2 + 1, mid + 1, r);tree[idx] = min(tree[idx * 2], tree[idx * 2 + 1]);}void push_down(int idx, int l, int r){if(lazy[idx] != 0){int mid = (l + r) / 2;lazy[idx * 2] += lazy[idx];lazy[idx * 2 + 1] += lazy[idx];tree[idx * 2] += lazy[idx];tree[idx * 2 + 1] += lazy[idx];lazy[idx] = 0;}}void update(int idx, int l, int r, int ql, int qr, ll val){if(ql > r || qr < l)return;if(ql <= l && r <= qr){tree[idx] += val;lazy[idx] += val;return;}push_down(idx, l, r);int mid = (l + r) / 2;update(idx * 2, l, mid, ql, qr, val);update(idx * 2 + 1, mid + 1, r, ql, qr, val);tree[idx] = min(tree[idx * 2], tree[idx * 2 + 1]);}ll queryMin(int idx, int l, int r, int ql, int qr){if(ql > r || qr < l)return INF;if(ql <= l && r <= qr)return tree[idx];push_down(idx, l, r);int mid = (l + r) / 2;return min(queryMin(idx * 2, l, mid, ql, qr), queryMin(idx * 2 + 1, mid + 1, r, ql, qr));}// 返回区间[ql,qr]内,第一个值为0的下标,如果不存在返回 -1int q_first0(int idx, int l, int r, int ql, int qr){if(ql > r || qr < l)return -1;if(ql <= l && r <= qr){if(tree[idx] > 0)return -1; // 此区间没有0// 否则往下找if(l == r)return l;}push_down(idx, l, r);int mid = (l + r) / 2;int res = q_first0(idx * 2, l, mid, ql, qr);if(res != -1)return res;return q_first0(idx * 2 + 1, mid + 1, r, ql, qr);}
};
// 辅助函数:在一个排好序的偶数下标数组 even_idx 中,给定原区间 [L, R],返回在偶数数组中的左右下标区间
pair<int, int> get(int L, int R)
{int l = int(lower_bound(even_idx.begin(), even_idx.end(), L) - even_idx.begin());int r = int(upper_bound(even_idx.begin(), even_idx.end(), R) - even_idx.begin()) - 1;if(l > r)return {-1, -1};return {l, r};
}
struct ST_e
{int n;vector<ll> tree, lazy;ST_e(int n) : n(n){tree.assign(4 * n, INF);lazy.assign(4 * n, 0);}void build(int idx, int l, int r){if(l == r){tree[idx] = even_a[l - 1];return;}int mid = (l + r) / 2;build(idx * 2, l, mid);build(idx * 2 + 1, mid + 1, r);tree[idx] = min(tree[idx * 2], tree[idx * 2 + 1]);}void push_down(int idx, int l, int r){if(lazy[idx] != 0){int mid = (l + r) / 2;lazy[idx * 2] += lazy[idx];lazy[idx * 2 + 1] += lazy[idx];tree[idx * 2] += lazy[idx];tree[idx * 2 + 1] += lazy[idx];lazy[idx] = 0;}}void update(int idx, int l, int r, int ql, int qr, ll val){if(ql > r || qr < l)return;if(ql <= l && r <= qr){tree[idx] += val;lazy[idx] += val;return;}push_down(idx, l, r);int mid = (l + r) / 2;update(idx * 2, l, mid, ql, qr, val);update(idx * 2 + 1, mid + 1, r, ql, qr, val);tree[idx] = min(tree[idx * 2], tree[idx * 2 + 1]);}ll queryMin(int idx, int l, int r, int ql, int qr){if(ql > r || qr < l)return INF;if(ql <= l && r <= qr)return tree[idx];push_down(idx, l, r);int mid = (l + r) / 2;return min(queryMin(idx * 2, l, mid, ql, qr), queryMin(idx * 2 + 1, mid + 1, r, ql, qr));}
};
int n,m;
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;for(int i = 0; i < n; i++)cin >> a[i];// 建立两个线段树ST ST_all(n);ST_all.build(1, 1, n);for(int i = 1; i <= n; i++){if(i % 2 == 0){even_idx.push_back(i);even_a.push_back(a[i - 1]);}}int e_cnt = even_idx.size();ST_e ST_even(e_cnt);if(e_cnt > 0)ST_even.build(1, 1, e_cnt);// 每回合操作// 注意:每回合更新都是对当前状态累积的while(m--){int l, r;ll k;cin >> l >> r >> k;// 更新主线段树ST_all.update(1, 1, n, l, r, k);// 更新偶数下标树:更新所有偶数 i in [l, r]if(e_cnt > 0){// 在 even_idx 中寻找落在 [l, r] 的下标位置auto pr = get(l, r);if(pr.first != -1){ST_even.update(1, 1, e_cnt, pr.first + 1, pr.second + 1, k);}}// 查询第一个值为0的下标int first0 = ST_all.q_first0(1, 1, n, 1, n);int j; // j为自然状态下箱子中最小缺失数字,下标从1开始,如果都非0,则 j = n+1if(first0 == -1){j = n + 1;}else{j = first0;}// 如果 j == 1,则必定 b1==0,无论如何都无法获得胜利if(j == 1){cout << -1 << endl;continue;}// 自然状态下,缺失数字为 j// 如果 j 为偶数,则 Thephix 获胜,代价为0if(j % 2 == 0){cout << 0 << endl;continue;}// j 为奇数,则需要利用魔法取出某个偶数 i (< j) 的球,代价为 b_i// 在区间[1, j-1]中偶数下标集合:实际上就是偶数在区间 [2, j-1]if(j - 1 < 2){// 区间中没有偶数cout << -1 << endl;continue;}// 在 even_idx 中查找属于 [2, j-1] 的下标auto pr = get(2, j - 1);if(pr.first == -1){cout << -1 << endl;continue;}ll ans = ST_even.queryMin(1, 1, e_cnt, pr.first + 1, pr.second + 1);// ans 为区间中最小的球数,也即移除该偶数所需代价cout << ans << endl;}cout << "\n";return 0;
}
官解: