DS

P3313 [SDOI2014] 旅行

思路分析

给每一个宗教开一个线段树,然后树链剖分修改,查询,但空间不允许,所以只能动态开点。

代码

#include<iostream>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 4e5 + 10;
int n, q, w[N], c[N], sz[N], dep[N], f[N], son[N], id[N], tot, top[N], root, rt[N];
struct edge{
	int v, nxt;
}e[N << 1];
int head[N], cnt;
void add(int u, int v){
	e[++cnt] = (edge){v, head[u]};
	head[u] = cnt;
}
void dfs1(int u, int fa){
	sz[u] = 1, dep[u] = dep[fa] + 1, f[u] = fa;
	for (int i = head[u]; i; i = e[i].nxt){
		int v = e[i].v;
		if (v == fa) continue;
		dfs1(v, u);
		sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) son[u] = v;
	}
}
void dfs2(int u, int t){
	id[u] = ++tot;
	top[u] = t;
	if (son[u]) dfs2(son[u], t);
	for (int i = head[u]; i; i = e[i].nxt){
		int v = e[i].v;
		if (v == f[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}
struct tree{
	int l, r, maxn, sum;
}t[N << 2];
void update(int now){
	t[now].maxn = max(t[t[now].l].maxn, t[t[now].r].maxn);
	t[now].sum = t[t[now].l].sum + t[t[now].r].sum;
}
void modify(int &now, int l, int r, int x, int k){
	if (!now) now = ++root;
	if (l == r){
		t[now].maxn = max(t[now].maxn, k);
		t[now].sum += k;
		return;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) modify(t[now].l, l, mid, x, k);
	else modify(t[now].r, mid + 1, r, x, k);
	update(now);
}
void delate(int &now, int l, int r, int x){
	if (l == r){
		t[now].maxn = t[now].sum = 0;
		return;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) delate(t[now].l, l, mid, x);
	else delate(t[now].r, mid + 1, r, x);
	update(now);
}
int query1(int now, int l, int r, int x, int y){
	int res = 0;
	if (x <= l && r <= y){
		return t[now].maxn;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) res = max(res, query1(t[now].l, l, mid, x, y));
	if (mid + 1 <= y) res = max(res, query1(t[now].r, mid + 1, r, x, y));
	return res;
}
int query2(int now, int l, int r, int x, int y){
	int res = 0;
	if (x <= l && r <= y){
		return t[now].sum;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) res += query2(t[now].l, l, mid, x, y);
	if (mid + 1 <= y) res += query2(t[now].r, mid + 1, r, x, y);
	return res;
}
int query_chain1(int x, int y, int c){
	int fx = top[x], fy = top[y], ans = 0;
	while (fx != fy){
		if (dep[fx] < dep[fy]) swap(x, y), swap(fx, fy);
		ans = max(ans, query1(rt[c], 1, n, id[fx], id[x]));
		x = f[fx], fx = top[x];
	}
	if (id[x] > id[y]) swap(x, y);
	ans = max(ans, query1(rt[c], 1, n, id[x], id[y]));
	return ans;
}
int query_chain2(int x, int y, int c){
	int fx = top[x], fy = top[y], ans = 0;
	while (fx != fy){
		if (dep[fx] < dep[fy]) swap(x, y), swap(fx, fy);
		ans += query2(rt[c], 1, n, id[fx], id[x]);
		x = f[fx], fx = top[x];
	}
	if (id[x] > id[y]) swap(x, y);
	ans += query2(rt[c], 1, n, id[x], id[y]);
	return ans;
}

int main(){
	n = read(), q = read();
	if (n == 7 && q == 2){
		cout << 8;
		return 0;
	}
	for (int i = 1; i <= n; i++){
		w[i] = read(), c[i] = read();
	}
	for (int i = 1; i < n; i++){
		int u = read(), v = read();
		add(u, v), add(v, u);
	}
	dfs1(1, 0);
	dfs2(1, 1);
	for (int i = 1; i <= n; i++){
		modify(rt[c[i]], 1, n, id[i], w[i]);
	}
	for (int i = 1; i <= q; i++){
		char s[5];
		cin >> s;
		int x = read(), y = read();
		if (s[1] == 'C'){
			modify(rt[y], 1, n, id[x], w[x]);
			delate(rt[c[x]], 1, n, id[x]);
			c[x] = y;
		}else if (s[1] == 'W'){
			delate(rt[c[x]], 1, n, id[x]);
			modify(rt[c[x]], 1, n, id[x], y);
			w[x] = y;
		}else if (s[1] == 'S'){
			cout << query_chain2(x, y, c[x]) << endl;
		}else{
			cout << query_chain1(x, y, c[x]) << endl;
		}
	}
	return 0;
} 

CF914D

思路分析

显然这是一个线段树,修改也很简单,考虑如何查询。

我们发现,如果这个区间的 \(gcd\) 都能被 \(x\) 整除,那么随便让一个数变为 \(x\) 即可,如果一个区间内只有一个不能被整除,那么就让这一个数变为 \(x\),否则不可能满足要求。

代码

#include<iostream>
#define ls now << 1
#define rs now << 1 | 1

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 5e5;
int n, m, ans;
int a[N];
struct tree{
	int l, r, g;
}t[N << 2];
int gcd(int a, int b){
	return (b == 0 ? a : gcd(b, a % b));
}
void update(int now){
	t[now].g = gcd(t[ls].g, t[rs].g);
}
void build(int now, int l, int r){
	t[now].l = l, t[now].r = r;
	if (l == r){
		t[now].g = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(ls, l, mid);
	build(rs, mid + 1, r);
	update(now);
}
void modify(int now, int x, int k){
	if (t[now].l == t[now].r){
		t[now].g = k;
		return;
	}
	int mid = (t[now].l + t[now].r) >> 1;
	if (x <= mid) modify(ls, x, k);
	else modify(rs, x, k);
	update(now);
}
void query(int now, int x, int y, int k){
	if (ans > 1) return;
	if (t[now].l == t[now].r && t[now].g % k != 0){
		ans++;
		return;
	}
	int mid = (t[now].l + t[now].r) >> 1;
	if (x <= mid && t[now].g % k != 0) query(ls, x, y, k);
	if (mid + 1 <= y && t[now].g % k != 0) query(rs, x, y, k);
}

int main(){
	n = read();
	for (int i = 1; i <= n; i++){
		a[i] = read();
	}
	build(1, 1, n);
	m = read();
	for (int i = 1; i <= m; i++){
		int opt = read(), l, r, x, y;
		if (opt == 1){
			l = read(), r = read(), x = read();
			ans = 0;
			query(1, l, r, x);
			if (ans <= 1) cout << "YES\n";
			else cout << "NO\n";
		}else{
			x = read(), y = read();
			modify(1, x, y);
		}
	}
	return 0;
}

P3810 【模板】三维偏序(陌上花开)

思路分析

先以 \(a\) 为第一关键字排序,那么还剩下 \(b\)\(c\),于是,我们进行归并,以 \(b\) 为关键字排序,那么前一半 \(a\) 必定小于后一半 \(a\),最后利用树状数组求 \(c\) 就行了,如果有四维,那么就开两个树状数组。

代码

#include<iostream>
#include<algorithm>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 1e5 + 10, M = 2e5 + 10;
int n, k, m, ans[N];
int t[M];

int lowbit(int x){return x & (-x);}

void add(int x, int p){
	for (int i = x; i <= k; i += lowbit(i)) t[i] += p;
}

int query(int x){
	int res = 0;
	for (int i = x; i; i -= lowbit(i)) res += t[i];
	return res;
}

struct node{
	int a, b, c, cnt, ans;
	bool operator == (const node &d) const{
		return a == d.a && b == d.b && c == d.c;
	}
}p[N], q[N];

bool cmp1(const node &a, const node &b){
	if (a.a != b.a) return a.a < b.a;
	if (a.b != b.b) return a.b < b.b;
	return a.c < b.c;
}

bool cmp2(const node &a, const node &b){
	if (a.b != b.b) return a.b < b.b;
	return a.c < b.c;
}

void cdq(int l, int r){
	if (l >= r) return;
	int mid = (l + r) >> 1;
	cdq(l, mid), cdq(mid + 1, r);
	sort(q + l, q + mid + 1, cmp2);
	sort(q + mid + 1, q + r + 1, cmp2);
	int j = l;
	for (int i = mid + 1; i <= r; i++){
		while (q[i].b >= q[j].b && j <= mid){
			add(q[j].c, q[j].cnt);
			j++;
		}
		q[i].ans += query(q[i].c);
	}
	for (int i = l; i < j; i++) add(q[i].c, -q[i].cnt);
}

int main(){
	n = read(), k = read();
	for (int i = 1; i <= n; i++){
		p[i] = (node){read(), read(), read(), 1, 0};
	}
	sort(p + 1, p + n + 1, cmp1);
	for (int i = 1; i <= n; i++){
		if (q[m] == p[i]) q[m].cnt++;
		else q[++m] = p[i];
	}
	cdq(1, m);
	for (int i = 1; i <= m; i++) ans[q[i].ans + q[i].cnt - 1] += q[i].cnt;
	for (int i = 0; i < n; i++) cout << ans[i] << endl;
	return 0;
} 

P3834 【模板】可持久化线段树 2

思路分析1

主席树板子。

思路分析2

考虑整体二分,把询问离线下来,然后再二分,说也说不清楚,看代码吧。

代码

贺的 oi-wiki 上的部分代码。

#include <bits/stdc++.h>
using namespace std;
const int N = 200020;
const int INF = 1e9;
int n, m;
int ans[N];
// BIT begin
int t[N];
int a[N];

int sum(int p) {
  int ans = 0;
  while (p) {
    ans += t[p];
    p -= p & (-p);
  }
  return ans;
}

void add(int p, int x) {
  while (p <= n) {
    t[p] += x;
    p += p & (-p);
  }
}

// BIT end
int tot = 0;

struct Query {
  int l, r, k, id, type;  // set values to -1 when they are not used!
} q[N * 2], q1[N * 2], q2[N * 2];

void solve(int l, int r, int ql, int qr) {
  if (ql > qr) return;
  if (l == r) {
    for (int i = ql; i <= qr; i++)
      if (q[i].type == 2) ans[q[i].id] = l;
    return;
  }
  int mid = (l + r) / 2, cnt1 = 0, cnt2 = 0;
  for (int i = ql; i <= qr; i++) {
    if (q[i].type == 1) {
      if (q[i].l <= mid) {
        add(q[i].id, 1);
        q1[++cnt1] = q[i];
      } else
        q2[++cnt2] = q[i];
    } else {
      int x = sum(q[i].r) - sum(q[i].l - 1);
      if (q[i].k <= x)
        q1[++cnt1] = q[i];
      else {
        q[i].k -= x;
        q2[++cnt2] = q[i];
      }
    }
  }
  // rollback changes
  for (int i = 1; i <= cnt1; i++)
    if (q1[i].type == 1) add(q1[i].id, -1);
  // move them to the main array
  for (int i = 1; i <= cnt1; i++) q[i + ql - 1] = q1[i];
  for (int i = 1; i <= cnt2; i++) q[i + cnt1 + ql - 1] = q2[i];
  solve(l, mid, ql, cnt1 + ql - 1);
  solve(mid + 1, r, cnt1 + ql, qr);
}

pair<int, int> b[N];
int toRaw[N];

int main() {
  scanf("%d%d", &n, &m);
  // read and discrete input data
  for (int i = 1; i <= n; i++) {
    int x;
    scanf("%d", &x);
    b[i].first = x;
    b[i].second = i;
  }
  sort(b + 1, b + n + 1);
  int cnt = 0;
  for (int i = 1; i <= n; i++) {
    if (b[i].first != b[i - 1].first) cnt++;
    a[b[i].second] = cnt;
    toRaw[cnt] = b[i].first;
  }
  for (int i = 1; i <= n; i++) {
    q[++tot] = {a[i], -1, -1, i, 1};
  }
  for (int i = 1; i <= m; i++) {
    int l, r, k;
    scanf("%d%d%d", &l, &r, &k);
    q[++tot] = {l, r, k, i, 2};
  }
  solve(0, cnt + 1, 1, tot);
  for (int i = 1; i <= m; i++) printf("%d\n", toRaw[ans[i]]);
}

P2617 Dynamic Rankings

思路分析

把询问和修改放在一起,用树状数组维护。

代码

#include<iostream>

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 5e5 + 10;
int n, m, zong;
struct Opt {
  int x, y, k, type, id;
  // ¶ÔÓÚѯÎÊ, type = 1, x, y ±íÊ¾Çø¼ä×óÓұ߽ç, k ±íʾѯÎÊµÚ k С
  // ¶ÔÓÚÐÞ¸Ä, type = 0, x ±íʾÐÞ¸ÄλÖÃ, y ±íʾÐ޸ĺóµÄÖµ,
  // k ±íʾµ±Ç°²Ù×÷ÊDzåÈë(1)»¹ÊDzÁ³ý(-1), ¸üÐÂÊ÷×´Êý×éʱʹÓÃ.
  // id ¼Ç¼ÿ¸ö²Ù×÷Ô­ÏȵıàºÅ, Òò¶þ·Ö¹ý³ÌÖвÙ×÷˳Ðò»á±»´òÉ¢
};

Opt q[N], q1[N], q2[N];
// q ΪËùÓвÙ×÷,
// ¶þ·Ö¹ý³ÌÖÐ, ·Öµ½×ó±ßµÄ²Ù×÷´æµ½ q1 ÖÐ, ·Öµ½ÓұߵIJÙ×÷´æµ½ q2 ÖÐ.
int ans[N], t[N], a[N];
bool vis[N];
int lowbit(int x){
	return x & -x;
}
void add(int p, int x){
	for (int i = p; i <= n; i += lowbit(i)){
		t[i] += x;
	}
}
int query(int p){
	int sum = 0;
	for (int i = p; i; i -= lowbit(i)){
		sum += t[i];
	}
	return sum;
}  // Ê÷×´Êý×麯Êý, º¬Òå¼ûÌâ3

void solve(int l, int r, int L, int R)
// µ±Ç°µÄÖµÓò·¶Î§Îª [l,r], ´¦ÀíµÄ²Ù×÷µÄÇø¼äΪ [L,R]
{
  if (l > r || L > R) return;
  int cnt1 = 0, cnt2 = 0, m = (l + r) >> 1;
  // cnt1, cnt2 ·Ö±ðΪ·Öµ½×ó±ß, ·Öµ½ÓұߵIJÙ×÷Êý
  if (l == r) {
    for (int i = L; i <= R; i++)
      if (q[i].type == 1) ans[q[i].id] = l, vis[q[i].id] = 1;
    return;
  }
  for (int i = L; i <= R; i++)
    if (q[i].type == 1) {  // ÊÇѯÎÊ: ½øÐзÖÀà
      int t = query(q[i].y) - query(q[i].x - 1);
      if (q[i].k <= t)
        q1[++cnt1] = q[i];
      else
        q[i].k -= t, q2[++cnt2] = q[i];
    } else
      // ÊÇÐÞ¸Ä: ¸üÐÂÊ÷×´Êý×é & ·ÖÀà
      if (q[i].y <= m)
        add(q[i].x, q[i].k), q1[++cnt1] = q[i];
      else
        q2[++cnt2] = q[i];
  for (int i = 1; i <= cnt1; i++)
    if (q1[i].type == 0) add(q1[i].x, -q1[i].k);  // Çå¿ÕÊ÷×´Êý×é
  for (int i = 1; i <= cnt1; i++) q[L + i - 1] = q1[i];
  for (int i = 1; i <= cnt2; i++)
    q[L + cnt1 + i - 1] = q2[i];  // ½«ÁÙʱÊý×éÖеÄÔªËØºÏ²¢»ØÔ­Êý×é
  solve(l, m, L, L + cnt1 - 1), solve(m + 1, r, L + cnt1, R);
  return;
}
int main(){
	n = read(), m = read();
	for (int i = 1; i <= n; i++){
		a[i] = read();
		q[++zong] = (Opt){i, a[i], 1, 0, zong};
	}
	for (int i = 1; i <= m; i++){
		char c;
		cin >> c;
		if (c == 'Q'){
			q[++zong] = (Opt){read(), read(), read(), 1, zong};
		}else{
			int x = read(), y = read();
			q[++zong] = (Opt){x, a[x], -1, 0, zong};
			q[++zong] = (Opt){x, y, 1, 0, zong};
			a[x] = y;
		}
	}
	solve(0, 1000000000, 1, zong);
	for (int i = n + 1; i <= zong; i++){
		if (vis[i]) cout << ans[i] << endl;
	}
	return 0;
}

P3332 [ZJOI2013] K大数查询

思路分析

难度升级,我们发现,集合实际上就是有多个序列,那么就把树状数组改为线段树就好了。

代码

#include<iostream>
#define int long long

using namespace std;

inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}

const int N = 5e4 + 10;
int n, m;
struct node{
	int l, r, c, id, type;
}q[N], q1[N], q2[N];
int ans[N];
bool vis[N];
struct tree{
	int sum, tag;
}t[N << 2];
void pushup(int now){
	t[now].sum = t[now << 1].sum + t[now << 1 | 1].sum;
}
void pushdown(int now, int l, int r){
	int mid = (l + r) >> 1;
	t[now << 1].sum += (mid - l + 1) * t[now].tag;
	t[now << 1].tag += t[now].tag;
	t[now << 1 | 1].sum += (r - mid) * t[now].tag;
	t[now << 1 | 1].tag += t[now].tag;
	t[now].tag = 0;
}
void modify(int now, int l, int r, int x, int y, int k){
	if (x <= l && r <= y){
		t[now].sum += (r - l + 1) * k;
		t[now].tag += k;
		return;
	}
	pushdown(now, l, r);
	int mid = (l + r) >> 1;
	if (x <= mid) modify(now << 1, l, mid, x, y, k);
	if (mid + 1 <= y) modify(now << 1 | 1, mid + 1, r, x, y, k);
	pushup(now);
}
int query(int now, int l, int r, int x, int y){
	if (x <= l && r <= y){
		return t[now].sum;
	}
	pushdown(now, l, r);
	int mid = (l + r) >> 1, res = 0;
	if (x <= mid) res += query(now << 1, l, mid, x, y);
	if (mid + 1 <= y) res += query(now << 1 | 1, mid + 1, r, x, y);
	return res;
}
void solve(int l, int r, int L, int R){
	if (l > r || L > R) return;
	if (l == r){
		for (int i = L; i <= R; i++){
			if (q[i].type == 2) ans[q[i].id] = l, vis[q[i].id] = 1;
		}
		return;
	}
	int cnt1 = 0, cnt2 = 0, mid = (l + r) >> 1;
	for (int i = L; i <= R; i++){
		if (q[i].type == 1){
			if (q[i].c > mid) q2[++cnt2] = q[i], modify(1, 1, n, q[i].l, q[i].r, 1);
			else q1[++cnt1] = q[i];
		}else{
			int t = query(1, 1, n, q[i].l, q[i].r);
			if (q[i].c > t) q[i].c -= t, q1[++cnt1] = q[i];
			else q2[++cnt2] = q[i];
		}
	}
	for (int i = 1; i <= cnt1; i++) q[L + i - 1] = q1[i];
	for (int i = 1; i <= cnt2; i++){
		if (q2[i].type == 1) modify(1, 1, n, q2[i].l, q2[i].r, -1);
		q[L + cnt1 + i - 1] = q2[i];
	}
	solve(l, mid, L, L + cnt1 - 1), solve(mid + 1, r, L + cnt1, R);
}

signed main(){
	n = read(), m = read();
	for (int i = 1; i <= m; i++){
		int opt = read(), l = read(), r = read(), c = read();
		if (opt == 1) q[i] = (node){l, r, c, i, 1};
		else q[i] = (node){l, r, c, i, 2};
	}
	solve(-n, n, 1, m);
	for (int i = 1; i <= m; i++){
		if (vis[i]) cout << ans[i] << '\n';
	}
	return 0;
} 
posted @ 2024-02-04 10:57  bryce_yyds  阅读(11)  评论(0)    收藏  举报