if (r <= mid) return query(2 * u, l, r);
,递归的时候不是 query(2 * u, l, mid)
!#include
#include
#include
typedef unsigned long long ll;
using namespace std;
const int maxn = 1000010;
const ll M = 1e12;
int p[maxn];
ll sum[maxn];
int find(int x){
if (p[x] == x) return x;
return p[x] = find(p[x]);
}
void unite(int a, int b) {
if (find(a) == find(b)) return;
p[a] = find(b);
}
unsigned long long k1, k2;
int N;
long long a[1000001];
unsigned long long xorShift128Plus() {
unsigned long long k3 = k1, k4 = k2;
k1 = k4;
k3 ^= k3 << 23;
k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
return k2 + k4;
}
void gen() {
scanf("%d %llu %llu", &N, &k1, &k2);
for (int i = 1; i <= N; i++) {
a[i] = xorShift128Plus() % 999999999999 + 1;
}
}
struct node {
int l, r;
ll sum;
}tr[maxn * 4];
void pushup(int u) {
tr[u].sum = tr[2 * u].sum + tr[2 * u + 1].sum;
}
void build(int u, int l, int r) {
if (l == r) tr[u] = { l, l, 0 };
else {
tr[u].l = l, tr[u].r = r;
int mid = (l + r) / 2;
build(2 * u, l, mid), build(2 * u + 1, mid + 1, r);
//pushup(u);
}
}
node query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u];
int mid = (tr[u].l + tr[u].r) / 2;
if (r <= mid) return query(2 * u, l, r);
else if (l > mid) return query(2 * u + 1, l, r);
else {
node left = query(2 * u, l, r);
node right = query(2 * u + 1, l, r);
node res;
res.sum = left.sum + right.sum;
return res;
}
}
void modify(int u, int x, ll v) { // a[x] = v;
if (tr[u].l == x && tr[u].r == x) tr[u].sum = v;
else {
int mid = (tr[u].l + tr[u].r) / 2;
if (x <= mid) modify(2 * u, x, v);
else modify(2 * u + 1, x, v);
pushup(u);
}
}
int main() {
gen();
sort(a + 1, a + N + 1);
//for (int i = 1; i <= N; i++) printf("%llu ", a[i]);
for (int i = 1; i <= N; i++) {
sum[i] = sum[i - 1] + a[i];
}
int Q;
scanf("%d", &Q);
build(1, 1, N);
for (int i = 1; i <= N + 1; i++) {
p[i] = i;
}
while (Q--) {
char op[5];
ll x;
scanf("%s%llu", op, &x);
if (op[0] == 'D') { // 标记>=x的第一个未被标记的a[i]
int id = lower_bound(a + 1, a + N + 1, x) - a;
int pos = find(id);
if (pos == N + 1) continue;
unite(pos, pos + 1);
modify(1, pos, a[pos]);
}
if (op[0] == 'F') { // 查询>=x的第一个未被标记的a[i]
int id = lower_bound(a + 1, a + N + 1, x) - a;
int pos = find(id);
if (pos == N + 1) printf("%llu\n", M);
else {
printf("%llu\n", a[pos]);
}
}
if (op[0] == 'R') { // <=x的标记全部清零
int id = upper_bound(a + 1, a + N + 1, x) - a - 1;
if (id == 0) continue;
for (int i = 1; i <= id; i++) {
p[i] = i;
modify(1, i, 0);
}
}
if (op[0] == 'C') { //查询小于等于 x 的所有未标记数之和;若没有,则输出0。
int id = upper_bound(a + 1, a + N + 1, x) - a - 1;
if (id == 0) printf("0\n");
else {
printf("%llu\n", sum[id] - query(1, 1, id).sum);
}
}
}
return 0;
}
给出一个长度为 n 的序列,接下来有 m 次操作,每次操作分为下列两种类型:
这个题确实有几个可圈可点的地方
#include
#include
#include
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7, P = 131;
const int maxn = 500010;
ll p[maxn], a[maxn], sump[maxn];
struct node {
int l, r;
ll Hash, mmax, add;
}tr[maxn * 4];
void pushup(int u) {
tr[u].Hash = (tr[2 * u].Hash + tr[2 * u + 1].Hash) % mod;
tr[u].mmax = max(tr[2 * u].mmax, tr[2 * u + 1].mmax);
}
void pushdown(int u) {
auto& rt = tr[u], & l = tr[2 * u], & r = tr[2 * u + 1];
if (rt.add) {
//出现减法一定要 + mod 再 % mod
l.add += rt.add, l.Hash = (l.Hash + (sump[l.r] - sump[l.l - 1]) * rt.add % mod + mod) % mod, l.mmax += rt.add;
r.add += rt.add, r.Hash = (r.Hash + (sump[r.r] - sump[r.l - 1]) * rt.add % mod + mod) % mod, r.mmax += rt.add;
rt.add = 0;
}
}
void build(int u, int l, int r) {
if (l == r) tr[u] = { l, r, a[l] * p[l] % mod, a[l], 0 };
else {
tr[u].l = l, tr[u].r = r;
int mid = (l + r) / 2;
build(2 * u, l, mid), build(2 * u + 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) {
//这里的 Hash 别忘修改, 出现减法一定要 + mod 再 % mod
tr[u].Hash = (tr[u].Hash + sump[tr[u].r] - sump[tr[u].l - 1] + mod) % mod;
tr[u].add++;
tr[u].mmax++;
}
else {
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
if (l <= mid) modify(2 * u, l, r);
if (r > mid) modify(2 * u + 1, l, r);
pushup(u);
}
}
void modify_mod(int u) {
if (tr[u].mmax < 65536) return;
if (tr[u].l == tr[u].r) {
tr[u].mmax %= 65536;
tr[u].Hash = tr[u].mmax * p[tr[u].l] % mod;
}
else {
pushdown(u);
modify_mod(2 * u), modify_mod(2 * u + 1);
pushup(u);
}
}
ll query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].Hash;
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
ll sum = 0;
if (l <= mid) sum = (sum + query(2 * u, l, r)) % mod;
if (r > mid) sum = (sum + query(2 * u + 1, l, r)) % mod;
return sum;
}
bool check(int x, int y, int L) {
if (x > y) swap(x, y);
ll q1 = query(1, x, x + L - 1) * p[y - x] % mod;
ll q2 = query(1, y, y + L - 1);
if (q1 == q2) return true;
return false;
}
int main() {
int N, Q;
scanf("%d%d", &N, &Q);
p[0] = sump[0] = 1;
for (int i = 1; i <= N; i++) {
scanf("%lld", &a[i]);
p[i] = p[i - 1] * P % mod;
sump[i] = (sump[i - 1] + p[i]) % mod;
}
build(1, 1, N);
while (Q--) {
/*for (int i = 1; i <= N; i++) {
printf("%lld ", query(1, i, i));
}
printf("\n");*/
int op;
scanf("%d", &op);
if (op == 1) {
int l, r;
scanf("%d%d", &l, &r);
modify(1, l, r);
modify_mod(1);
}
else {
int x, y, L;
scanf("%d%d%d", &x, &y, &L);
if (check(x, y, L)) printf("yes\n");
else printf("no\n");
}
}
/*for (int i = 1; i <= N; i++) {
printf("%lld %lld\n", p[i], sump[i]);
}*/
return 0;
}
#include
#include
#include
#include
using namespace std;
const int maxn = 1000010;
int a[maxn];
struct node {
int l, r, maxc;
}tr[maxn * 2];
int N, C;
void pushup(int u) {
tr[u].maxc = max(tr[2 * u].maxc, tr[2 * u + 1].maxc);
}
void build(int u, int l, int r) {
if (l == r) {
tr[u] = { l, r, C };
}
else {
tr[u].l = l, tr[u].r = r;
int mid = (l + r) / 2;
build(2 * u, l, mid), build(2 * u + 1, mid + 1, r);
pushup(u);
}
}
bool modify(int u, int x) {
//printf("***\n");
if (tr[u].l == tr[u].r) {
tr[u].maxc -= x;
return true;
}
else {
if (tr[2 * u].maxc >= x && modify(2 * u, x)) {
pushup(u);
return true;
}
if (tr[2 * u + 1].maxc >= x && modify(2 * u + 1, x)) {
pushup(u);
return true;
}
return false;
}
}
int query(int u, int id) {
if (tr[u].l == tr[u].r) return tr[u].maxc;
int mid = (tr[u].l + tr[u].r) / 2;
if (id <= mid) return query(2 * u, id);
else if (id > mid) return query(2 * u + 1, id);
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &N, &C);
build(1, 1, N);
for (int i = 1; i <= N; i++) {
scanf("%d", &a[i]);
}
map<int, int> mp;
int res1 = 0, res2 = 0;
for (int i = 1; i <= N; i++) {
modify(1, a[i]);
auto p = mp.lower_bound(a[i]);
if (p != mp.end()) {
--p->second;
++mp[p->first - a[i]];
if (p->second == 0) mp.erase(p);
}
else {
++mp[C - a[i]];
++res2;
}
}
for (int i = 1; i <= N; i++) {
//printf("$$$ %d\n", query(1, i));
if (query(1, i) == C) {
res1 = i - 1;
break;
}
if (i == N) {
res1 = N;
}
}
printf("%d %d\n", res1, res2);
}
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 100010;
typedef long long ll;
int N, M, a[maxn];
ll sum2[maxn];
vector<int> nums;
struct node {
int l, r;
int cnt;
ll sum;
}tr[maxn * 4 + maxn * 17];
//空间开到 4N + N * log(N)
int root[maxn], idx;
int find(int x) {
return lower_bound(nums.begin(), nums.end(), x, greater<int>()) - nums.begin();
}
int build(int l, int r) {
int p = ++idx;
tr[p] = { l, r };
if (l == r) return p;
int mid = (l + r) / 2;
tr[p].l = build(l, mid), tr[p].r = build(mid + 1, r);
return p;
}
//p 为原来的线段树的结点,q 为复制的线段是的结点
int insert(int p, int l, int r, int x) {
int q = ++idx;
tr[q] = tr[p];
if (l == r) {
tr[q].cnt++;
tr[q].sum += nums[x];
return q;
}
int mid = (l + r) / 2;
if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x);
else tr[q].r = insert(tr[p].r, mid + 1, r, x);
tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
tr[q].sum = tr[tr[q].l].sum + tr[tr[q].r].sum;
return q;
}
ll query(int q, int p, int l, int r, int k) {
//这个地方要小心,因为有重复数字的话,需要这样处理,不然结果会偏大。
if (l == r) return (tr[q].sum - tr[p].sum) / (tr[q].cnt - tr[p].cnt) * k;
int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
int mid = (l + r) / 2;
if (k <= cnt) return query(tr[q].l, tr[p].l, l, mid, k);
else return tr[tr[q].l].sum - tr[tr[p].l].sum + query(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
}
int main() {
for (ll i = 1; i <= 100000; i++) {
sum2[i] = i * i + sum2[i - 1];
}
int T;
scanf("%d", &T);
while (T--) {
//看看如何初始化的
nums.clear();
for (int i = 0; i <= idx; i++) {
tr[i] = { 0, 0, 0, 0 };
}
fill(root, root + N + 1, 0);
idx = 0;
scanf("%d", &N);
for (int i = 1; i <= N; i++) {
scanf("%d", &a[i]);
nums.push_back(a[i]);
}
sort(nums.begin(), nums.end(), greater<int>());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
root[0] = build(0, nums.size() - 1);
for (int i = 1; i <= N; i++) {
root[i] = insert(root[i - 1], 0, nums.size() - 1, find(a[i]));
}
scanf("%d", &M);
while (M--) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
int m = r - l + 1;
printf("%lld\n", sum2[m] + query(root[r], root[l - 1], 0, nums.size() - 1, k));
}
}
return 0;
}
a[stk[tt]] == a[i]
的情况,就把这个 stk[tt] 记录下来。后面往单调栈里面推入数字的时候,不推入 i 而推入 stk[tt]。这样子,就算出现 5 3 5 5 3 5 这样子的东西,就算中间数字不是连续的,也是可以正确的找到离他最远的符合要求的那个数字。#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 100010, maxm = 200010;
int h[maxn], e[maxm], ne[maxm], w[maxm], idx;
int a[maxn], N, stk[maxn];
int din[maxn];
ll f[maxn];
void add(int a, int b, int c) {
swap(a, b);
din[b]++;
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
void build1() {
int tt = 0;
for (int i = 1; i <= N; i++) {
bool flag = false;
//小心 id 别设置成 bool 类型喽
int id;
while (tt && a[stk[tt]] < a[i]) tt--;
//小心别写成 tt & a[stk[tt]] == a[i] !!!
if (tt && a[stk[tt]] == a[i]) {
flag = true, id = stk[tt];
tt--;
}
if (tt) add(i, stk[tt], i - stk[tt]);
if (flag) {
stk[++tt] = id;
}
else stk[++tt] = i;
}
}
void build2() {
int tt = 0;
for (int i = N; i >= 1; i--) {
bool flag = false;
int id;
while (tt && a[stk[tt]] < a[i]) tt--;
if (tt && a[stk[tt]] == a[i]) {
flag = true, id = stk[tt];
tt--;
}
if (tt) add(i, stk[tt], stk[tt] - i);
if (flag) {
stk[++tt] = id;
}
else stk[++tt] = i;
}
}
void toposort() {
queue<int> que;
for (int i = 1; i <= N; i++) {
if (din[i] == 0) {
que.push(i);
}
}
while (que.size()) {
int u = que.front(); que.pop();
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
f[v] = max(f[v], f[u] + w[i]);
if (--din[v] == 0) que.push(v);
}
}
}
int main() {
memset(h, -1, sizeof h);
scanf("%d", &N);
for (int i = 1; i <= N; i++) scanf("%d", &a[i]);
build1();
build2();
toposort();
for (int i = 1; i <= N; i++) {
printf("%lld%c", f[i], i == N ? '\n' : ' ');
}
return 0;
}
#include
#include
#include
#include
using namespace std;
const int maxn = 200010;
struct node {
int l, r;
int mmin, se_min, num; //最小值,次小值,最小值个数
int sum[35];
}tr[maxn * 4];
int res_sum[35];
int w[maxn];
void change(int u, int pre, int cur) {
for (int i = 0; i < 30; i++) {
if ((pre >> i) & 1) tr[u].sum[i] -= tr[u].num;
if ((cur >> i) & 1) tr[u].sum[i] += tr[u].num;
}
}
void pushup(int u) {
tr[u].num = 0;
int left = 2 * u, right = 2 * u + 1;
for (int i = 0; i < 30; i++)
tr[u].sum[i] = tr[left].sum[i] + tr[right].sum[i];
tr[u].mmin = min(tr[left].mmin, tr[right].mmin);
tr[u].se_min = min(tr[left].se_min, tr[right].se_min);
//下面修改 tr[u].num的值,顺便更新 tr[u].se_min 的值
if (tr[u].mmin == tr[left].mmin) {
tr[u].num += tr[left].num;
}
else {
tr[u].se_min = min(tr[u].se_min, tr[left].mmin);
}
if (tr[u].mmin == tr[right].mmin) {
tr[u].num += tr[right].num;
}
else {
tr[u].se_min = min(tr[u].se_min, tr[right].mmin);
}
}
void pushdown(int u) {
//改变mmin和sum
int left = 2 * u, right = 2 * u + 1;
if (tr[u].mmin > tr[left].mmin) {
change(left, tr[left].mmin, tr[u].mmin);
tr[left].mmin = tr[u].mmin;
}
if (tr[u].mmin > tr[right].mmin) {
change(right, tr[right].mmin, tr[u].mmin);
tr[right].mmin = tr[u].mmin;
}
}
void build(int u, int l, int r) {
if (l == r) {
tr[u].l = l, tr[u].r = r;
tr[u].mmin = w[l], tr[u].se_min = 2e9, tr[u].num = 1;
change(u, 0, w[l]);
}
else {
tr[u].l = l, tr[u].r = r;
int mid = (l + r) / 2;
build(2 * u, l, mid), build(2 * u + 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int l, int r, int x) {
if (x <= tr[u].mmin) return;
if (l <= tr[u].l && tr[u].r <= r && x < tr[u].se_min) {
//因为 x < tr[u].se_min,因此更新后最小值的数目不变。
change(u, tr[u].mmin, x);
tr[u].mmin = x;
return;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
if (l <= mid) modify(2 * u, l, r, x);
if (r > mid) modify(2 * u + 1, l, r, x);
pushup(u);
}
void query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) {
for (int i = 0; i < 30; i++) {
res_sum[i] += tr[u].sum[i];
}
return;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
if (l <= mid) query(2 * u, l, r);
if (r > mid) query(2 * u + 1, l, r);
}
int main() {
int N, Q;
scanf("%d%d", &N, &Q);
for (int i = 1; i <= N; i++) scanf("%d", &w[i]);
build(1, 1, N);
while (Q--) {
int op, l, r, x;
scanf("%d%d%d%d", &op, &l, &r, &x);
if (op == 1) {
modify(1, l, r, x);
}
else {
memset(res_sum, 0, sizeof res_sum);
query(1, l, r);
int res = x;
for (int i = 0; i < 30; i++) {
if (res_sum[i] & 1) res ^= (1 << i);
}
if (!res) {
printf("0\n");
continue;
}
int h = log2(res);
printf("%d\n", res_sum[h] + ((x >> h) & 1));
}
}
return 0;
}
J题实际上是本场唯一的一个套路题,前半考察对nim游戏的基础理解,后半考察Segment Beats。我组题会偏向于在比赛中设置这么一个题目,可以说是给那些有准备的队伍一个糖果。而实际上我之所以选用Segment Beats就是因为在不久前ccpc网络赛中就出现过一个,可以理解为我在考察大家平时的补题情况。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll INF = 1e18;
const int maxn = 2010, maxm = 200010;
struct node {
int l, r, id;
ll val;
};
vector<node> q;
ll w[maxn], tr[maxn][maxn], ans[maxm];
int lowbit(int x) {
return x & -x;
}
bool cmp(const node& u, const node& v) {
return u.val < v.val || u.val == v.val && u.id < v.id;
}
int N, M;
void update(int x, int y, ll v) {
while (x) {
int z = y;
while (z <= N) {
tr[x][z] = max(tr[x][z], v);
z += z & -z;
}
x -= x & -x;
}
}
ll query(int x, int y) {
ll res = -INF;
while (x <= N) {
int z = y;
while (z) {
res = max(res, tr[x][z]);
z -= z & -z;
}
x += x & -x;
}
return res;
}
int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i++) {
fill(tr[i], tr[i] + N + 1, -INF);
}
for (int i = 1; i <= N; i++) {
scanf("%lld", &w[i]);
w[i] += w[i - 1];
}
for (int i = 1; i <= N; i++) {
for (int j = i; j <= N; j++) {
q.push_back({ i, j, 0, w[j] - w[i - 1] });
}
}
for (int i = 1; i <= M; i++) {
int l, r;
ll x;
scanf("%d%d%lld", &l, &r, &x);
q.push_back({ l, r, i, x });
}
sort(q.begin(), q.end(), cmp);
for (auto p : q) {
int l = p.l, r = p.r, id = p.id;
ll val = p.val;
if (!id) update(l, r, val);
else ans[id] = query(l, r);
}
for (int i = 1; i <= M; i++) {
if (ans[i] != -INF) printf("%lld\n", ans[i]);
else printf("NONE\n");
}
return 0;
}
#include
#include
#include
#include
using namespace std;
const int maxn = 100010, maxm = 30 * maxn;
int w[maxn], sz[maxm], son[maxm][2], idx;
int N, M;
const int lg = 29, INF = (1 << 30);
vector<vector<int>> kth;
void insert(int x) {
int p = 0;
for (int i = lg; i >= 0; i--) {
sz[p]++;
int u = ((x >> i) & 1);
if (!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
sz[p]++;
}
void dfs(int u, int d) {
int l = son[u][0], r = son[u][1];
if (!l && !r) {
kth[u][1] = 0;
return;
}
if (l) dfs(l, d - 1);
if (r) dfs(r, d - 1);
for (int i = 1; i <= sz[u]; i++) {
kth[u][i] = INF;
//如果当前的 S 的第 d 位是 0。
if (sz[l] >= i) kth[u][i] = min(kth[u][i], kth[l][i]);
//那么答案就在右子树里面
else kth[u][i] = min(kth[u][i], (1 << d) ^ kth[r][i - sz[l]]);
//如果当前的 S 的第 d 位是 1.
if (sz[r] >= i) kth[u][i] = min(kth[u][i], kth[r][i]);
else kth[u][i] = min(kth[u][i], (1 << d) ^ kth[l][i - sz[r]]);
}
}
void build() {
//用resize,防止爆空间。
for (int i = 1; i <= N; i++) {
insert(w[i]);
}
kth.resize(idx + 1);
for (int i = 0; i <= idx; i++) {
kth[i].resize(sz[i] + 1);
}
for (int i = 0; i <= N; i++) {
kth[0][i] = INF;
}
dfs(0, lg);
}
int walk(int& p, int& k, int t) {
if (son[p][t] && sz[son[p][t]] >= k) {
p = son[p][t];
return 0;
}
else {
if (son[p][t]) k -= sz[son[p][t]];
p = son[p][1 ^ t];
return 1;
}
}
int query(int x, int lim, int k, int flag) {
int p = 0, S = 0, res = INF;
for(int i = lg; i >= 0; i--){
int t = ((x >> i) & 1);
//L的第lim的位置一定是0,R第lim的位置一定是1.
//我们让第lim的位置仍然贴合下界(上界)
//因此,从第 lim - 1 的位置,就可以0和1随便填了。
if(i < lim && t == flag){
//t==flag,比如L这一位是0的时候,那么S的这一位可以填1也可以填0
//当L这一位是1的时候,S的这一位只能填1. 因为要贴合下界。对于R也是同理
int tp = p, ts = S, tk = k;
//这一步表示不贴和下界的情况,由于后面的数可以随意填,res 就是当前的S,后面拼上 t^1,然后再拼上 kth[p的儿子][k].
if(walk(tp, tk, t ^ 1)) ts ^= (1 << i);
res = min(res, ts ^ kth[tp][tk]);
}
//这一步表示贴合下界(上界)的情况。
if(walk(p, k, t)) S ^= (1 << i);
}
//S贴合下界的情况要在最后一步更写一下答案。
res = min(res, S ^ kth[p][k]);
return res;
}
int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i++) scanf("%d", &w[i]);
build();
while (M--) {
int L, R, K;
scanf("%d%d%d", &L, &R, &K);
int p = lg;
while (p >= 0 && ((L >> p) & 1) == ((R >> p) & 1)) p--;
printf("%d\n", min(query(L, p, K, 0), query(R, p, K, 1)));
}
return 0;
}
法一:
#include
#include
#include
using namespace std;
const int maxn = 100010;
int w[maxn];
typedef long long ll;
struct node
{
int l, r, mmin;
}tr[maxn * 4];
int N, M;
void pushup(int u)
{
tr[u].mmin = min(tr[2 * u].mmin, tr[2 * u + 1].mmin);
}
void build(int u, int l, int r)
{
tr[u] = {l, r};
if(l == r){
tr[u].mmin = w[l];
}
else{
int mid = (l + r) / 2;
build(2 * u, l, mid), build(2 * u +1, mid +1, r);
pushup(u);
}
}
int query(int u, int L, int R)
{
if(L <= tr[u].l && tr[u].r <= R){
return tr[u].mmin;
}
int res = (1LL << 30) - 1;
int mid = (tr[u].l + tr[u].r) / 2;
if(L <= mid) res = query(2 * u, L, R);
if(R > mid) res = min(res, query(2 * u + 1, L, R));
return res;
}
void update(int u, int x, int y)
{
if(tr[u].l == x && tr[u].r == x){
tr[u].mmin = y;
return;
}
int mid = (tr[u].l + tr[u].r) / 2;
if(x <= mid) update(2 * u, x, y);
else update(2 * u +1, x, y);
pushup(u);
}
int main()
{
scanf("%d%d", &N, &M);
for(int i = 1; i <= N; i++){
scanf("%d", &w[i]);
}
build(1, 1, N);
while(M--){
int opt, x, y;
scanf("%d", &opt);
if(opt == 1){
scanf("%d%d", &x, &y);
w[x] = y;
update(1, x, y);
}
else{
scanf("%d", &x);
int lb = 1, ub = N - x + 1;
while(ub > lb){
int mid = (lb + ub + 1) / 2;
if(query(1, x, x + mid - 1) >= w[x]) lb = mid;
else ub = mid - 1;
}
int ans1 = lb;
lb = 1, ub = x;
while(ub > lb){
int mid = (lb + ub + 1) / 2;
if(query(1, x - mid + 1, x) >= w[x]) lb = mid;
else ub = mid - 1;
}
int ans2 = lb;
//printf("*** %d %d\n", ans1, ans2);
printf("%lld\n", (ll)ans1 * (ll)ans2);
}
}
return 0;
}
法二:
具体做法:只考虑求 a x a_x ax左边的值,右边类似
1.因为要尽量离 a x a_x ax近,那么贪心的检查右子树中是否含 x x x。有 并且 区间最小值 a x a_x ax 的话. 那么右边可能存在答案(1.2为什么说可能,因为可能存在 唯一 一个 v a l < a x val < a_x val<ax,其位置 > x > x >x. 这样答案就不存在,但是我们还是会往右子树走.)
2.如果右子树不符合要求,自然就往左子树看。若左子树最小值 < a x < a_x <ax. 那就往左子树搜索(这个时候答案是一定存在的).
根据:只要往左走,那么答案就一定存在。考虑其生成树的样子。要么是一直往右走。要么只能往左拐一下就直接找到答案。那么单次查询最差也就两条链。即 O ( 2 log n ) O(2\log n) O(2logn) 的复杂度.
————————————————
版权声明:本文为CSDN博主「Implicit_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35577488/article/details/109923150
#include
#include
#include
using namespace std;
const int maxn = 100010;
int w[maxn];
typedef long long ll;
struct node
{
int l, r, mmin;
}tr[maxn * 4];
int N, M;
void pushup(int u)
{
tr[u].mmin = min(tr[2 * u].mmin, tr[2 * u + 1].mmin);
}
void build(int u, int l, int r)
{
tr[u] = {l, r};
if(l == r){
tr[u].mmin = w[l];
}
else{
int mid = (l + r) / 2;
build(2 * u, l, mid), build(2 * u +1, mid +1, r);
pushup(u);
}
}
void update(int u, int x, int y)
{
if(tr[u].l == x && tr[u].r == x){
tr[u].mmin = y;
return;
}
int mid = (tr[u].l + tr[u].r) / 2;
if(x <= mid) update(2 * u, x, y);
else update(2 * u +1, x, y);
pushup(u);
}
int query1(int u, int L, int R)
{
if(tr[u].l == tr[u].r){
if(tr[u].mmin >= w[R]) return 0;
return tr[u].l;
}
int mid = (tr[u].l + tr[u].r) / 2;
if(R > mid && tr[2 * u + 1].mmin < w[R]){
int res = query1(2 * u + 1, L, R);
if(res != 0) return res;
}
if(L <= mid && tr[2 * u].mmin < w[R]){
return query1(2 * u, L, R);
}
return 0;
}
int query2(int u, int L, int R)
{
if(tr[u].l == tr[u].r){
if(tr[u].mmin >= w[L]) return N + 1;
return tr[u].l;
}
int mid = (tr[u].l + tr[u].r) / 2;
if(L <= mid && tr[2 * u].mmin < w[L]){
int res = query2(2 * u, L, R);
if(res != N + 1) return res;
}
if(R > mid && tr[2 * u + 1].mmin < w[L]){
return query2(2 * u + 1, L, R);
}
return N + 1;
}
int main()
{
scanf("%d%d", &N, &M);
for(int i = 1; i <= N; i++){
scanf("%d", &w[i]);
}
build(1, 1, N);
while(M--){
int opt, x, y;
scanf("%d", &opt);
if(opt == 1){
scanf("%d%d", &x, &y);
w[x] = y;
update(1, x, y);
}
else{
scanf("%d", &x);
ll ans1 = x - query1(1, 1, x), ans2 = query2(1, x, N) - x;
printf("%lld\n", ans1 * ans2);
}
}
return 0;
}
#include
using namespace std;
const int maxn = 200010, INF = 1e9;
struct node
{
//mi[i] 表示从i开始,包含 1~x的最短区间,的右端点。
int l, r;
int mi, len, lazy;
}tr[maxn * 4];
int N, M;
vector<int> w[maxn];
void pushup(int u)
{
tr[u].mi = min(tr[2 * u].mi, tr[2 * u + 1].mi);
tr[u].len = min(tr[2 * u].len, tr[2 * u + 1].len);
}
void pushdown(int u)
{
int t = tr[u].lazy;
if(t){
tr[2 * u].mi = tr[2 * u].lazy = t, tr[2 * u].len = t - tr[2 * u].r + 1;
tr[2 * u + 1].mi = tr[2 * u + 1].lazy = t, tr[2 * u + 1].len = t - tr[2 * u + 1].r + 1;
tr[u].lazy = 0;
}
}
void build(int u, int l, int r)
{
tr[u].l = l, tr[u].r = r, tr[u].len = INF;
tr[u].mi = tr[u].lazy = 0;
if(l == r) return;
int mid = (l + r) / 2;
build(2 * u, l, mid), build(2 * u + 1, mid + 1, r);
}
void modify(int u, int l, int r, int c)
{
//if(tr[u].r < l || tr[u].l > r) return;
if(l <= tr[u].l && tr[u].r <= r){
tr[u].mi = tr[u].lazy = c;
tr[u].len = c - tr[u].r + 1;
return;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
if(l <= mid) modify(2 * u, l, r, c);
if(r > mid) modify(2 * u + 1, l, r, c);
pushup(u);
}
//找到 [l, r] 中小于 last 的最大位置。
//即 如果mi[k]
//因为last - mi[k] + 1 作为以 i 为左端点len,只能作为在 [pre, cnt] 的答案。
int find(int u, int l, int r, int last)
{
if(tr[u].mi >= last) return -1;
if(tr[u].l == tr[u].r) return tr[u].l;
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
int tmp = -1;
tmp = find(2 * u + 1, l, r, last);
if(tmp == -1 && l <= mid) tmp = find(2 * u, l, r, last);
return tmp;
}
int main()
{
scanf("%d%d", &N, &M);
for(int i = 1, x; i <= N; i++){
scanf("%d", &x);
w[x].push_back(i);
}
build(1, 1, N);
for(int i = 1; i <= M; i++){
int pre = 1, sz = w[i].size();
for(int j = 0; j < sz; j++){
int last = w[i][j];
int cnt = find(1, pre, last, last);
if(cnt != -1) modify(1, pre, cnt, last);
pre = last + 1;
}
if(w[i].back() + 1 <= N) modify(1, w[i].back() + 1, N, INF);
printf("%d ", tr[1].len);
}
return 0;
}
#include
using namespace std;
const int maxn = 200010;
typedef long long ll;
ll a[maxn], b[maxn], s[maxn];
int N, M;
struct node
{
int l, r;
ll sum, a_s, add;
}tr[maxn * 4];
void pushup(int u)
{
tr[u].a_s = max(tr[2 * u].a_s, tr[2 * u + 1].a_s);
tr[u].sum = tr[2 * u].sum + tr[2 * u + 1].sum;
}
void pushdown(int u)
{
auto & rt = tr[u], &left = tr[2 * u], & right = tr[2 * u + 1];
if(rt.add){
left.add += rt.add, left.a_s -= rt.add;
right.add += rt.add, right.a_s -= rt.add;
rt.add = 0;
}
}
void build(int u, int l, int r)
{
tr[u] = {l, r};
tr[u].add = 0;
if(l == r){
tr[u].sum = b[l], tr[u].a_s = a[l] - s[l];
}
else{
int mid = (l + r) / 2;
build(2 * u, l, mid), build(2 * u + 1, mid + 1, r);
pushup(u);
}
}
void modify1(int u, int x, int add)
{
if(tr[u].l == x && tr[u].r == x){
tr[u].a_s += add;
}
else{
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
if(x <= mid) modify1(2 * u, x, add);
else modify1(2 * u + 1, x, add);
pushup(u);
}
}
void modify2(int u, int x, int y)
{
if(tr[u].l == x && tr[u].r == x){
tr[u].sum = y;
}
else{
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
if(x <= mid) modify2(2 * u, x, y);
else modify2(2 * u + 1, x, y);
pushup(u);
}
}
void modify3(int u, int l, int r, int add)
{
if(l <= tr[u].l && tr[u].r <= r){
tr[u].a_s -= add;
tr[u].add += add;
}
else{
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
if(l <= mid) modify3(2 * u, l, r, add);
if(r > mid) modify3(2 * u + 1, l, r, add);
pushup(u);
}
}
ll query1(int u, int l, int r)
{
if(l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
ll sum = 0;
if(l <= mid) sum += query1(2 * u, l, r);
if(r > mid) sum += query1(2 * u + 1, l, r);
return sum;
}
ll query2(int u, int l, int r)
{
if(l <= tr[u].l && tr[u].r <= r){
return tr[u].a_s;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
ll res = -1e18;
if(l <= mid) res = max(res, query2(2 * u, l, r));
if(r > mid) res = max(res, query2(2 * u + 1, l, r));
return res;
}
int main()
{
while(cin >> N >> M){
for(int i = 1; i <= N; i++){
scanf("%lld", &a[i]);
}
for(int i = 1; i <= N; i++){
scanf("%lld", &b[i]);
s[i] = b[i] + s[i - 1];
}
build(1, 1, N);
while(M--){
int op, x;
ll y;
scanf("%d", &op);
if(op == 1){
scanf("%d%lld", &x, &y);
modify1(1, x, y - a[x]);
a[x] = y;
}
else if(op == 2){
scanf("%d%lld", &x, &y);
modify2(1, x, y);
modify3(1, x, N, y - b[x]);
b[x] = y;
}
else{
scanf("%d", &x);
ll s_x = query1(1, 1, x);
//printf("Im here01\n");
ll a_s = query2(1, 1, x);
ll res = max(s_x, s_x + a_s);
//printf("Im here02\n");
printf("%lld\n", res);
}
}
}
return 0;
}
u = r[r[r[i]]];
。可以联想最近公共祖先。用树上倍增的方法快速寻找。#include
using namespace std;
const int N = 100010;
int prime[N], cnt;
int st[N], r[N], fa[N][17], depth[N], a[N];
vector<int> ps[N], divisors[N];
void sieve(int n)
{
for(int i = 2; i <= n; i++){
if(!st[i]) prime[cnt++] = st[i] = i;
for(int j = 0; prime[j] <= N / i; j++){
st[prime[j] * i] = prime[j];
if(i % prime[j] == 0) break;
}
}
for(int i = 2; i <= n; i++){
int x = i;
while(x > 1){
int v = st[x];
divisors[i].push_back(v);
while(x % v == 0) x /= v;
}
}
}
int main()
{
sieve(N - 1);
int n, q;
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++){
for(auto p : divisors[a[i]]) {
//printf("*** %d %d %d\n", p, a[i], i);
ps[p].push_back(i);
}
}
for(int i = 0; i < cnt; i++){
int p = prime[i];
ps[p].push_back(n + 1);
}
//根节点在 n + 1,根节点所有祖先都置为 n + 1.
r[n + 1] = fa[n + 1][0] = n + 1;
for(int i = 1; i <= 16; i++) fa[n + 1][i] = n + 1;
for(int i = n; i; i--){
r[i] = r[i + 1];
for(auto p : divisors[a[i]]){
r[i] = min(r[i], *upper_bound(ps[p].begin(), ps[p].end(), i));
}
fa[i][0] = r[i], depth[i] = depth[r[i]] + 1;
for(int k = 1; k <= 16; k++) fa[i][k] = fa[fa[i][k - 1]][k - 1];
}
while(q--){
int L, R;
scanf("%d%d", &L, &R);
int u = L;
for(int k = 16; k >= 0; k--){
if(fa[u][k] <= R){
u = fa[u][k];
}
}
printf("%d\n", depth[L] - depth[u] + 1);
}
//for(int i = 1; i <= n; i++) printf("%d ", r[i]);
//cout << endl;
//for(int i = 0; i <= 16; i++) printf("%d %d\n", i, fa[3][i]);
return 0;
}