「LibreOJ NOIラウンド#1」北朝鮮は、学校の思い出外(+ +ツリーラインを倍増自然を見て)

https://loj.ac/problem/510

ため\(N <= 100000 \) \(X \)\(X +ロー(X) \) フォレスト内のフォームに接続されたエッジ、各時間は、ルートにチェーン、およびクエリ点を修正することです。

以下のために(N \)\偉大な、私たちはより深い何かを考える必要があります。

なぜ考えてみましょう\(K> 2 \)の追加サイクルを何度とき。

例えば(= K 3 \)\、最小スタートビット\(= 1 \) それぞれが\(〜MOD〜2 * K \)、1,2,1,2サイクルに達します(\ Oを(N-)\)

、kが奇数で検討サイクルであったであろう(\ X-)を\(X +ロー(X)\ \) が(存在するもう一方の側に存在挿入することができるので、実際には鎖のいくつかの部分である森林を形成します2逆)。

場合\(kは\)奇数ではなく、これを楽しみプラスを増加させることができるだけでなく0になり、より高い位置へのジャンプ、一点は、複数の入力端を有していてもよいです。

実際には、2を含むインデックスのみを考慮する(> = kは\含ま)\無用部2 k個に相当する複数のストランドです。

そして場所に、インデックスは上含まれている2回にログを取る\(> = kは\が含まれている)、または次の層の真ん中にジャンプします。

だから、任意の時点から、行く(ログ\)\回はチェーンに来ます。

チェーンは、セグメントツリーのメンテナンス処方箋を動的にすることができ、疑問がチェーンの始まりを見つける方法ですか?

乗算は:前処理\(F [I] [J ] \) 、高い有効Iであることを特徴とする( - 2 ^ J \)\に、ジャンプ、各ジャンプ。

時間の複雑さ:\(^ログ2 \)

コード:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

int calc(int x) {
	int s = 0;
	while(x % 2 == 0) x /= 2, s ++;
	return s;
}

const int M = 19260817;
const int N = (2e5 + 5) * 31;

struct hash {
	int fi[M];
	int h[N]; int nt[N], c[N], tot;
	int& operator[] (int n) {
		int y = n % M;
		for(int p = fi[y]; p; p = nt[p])
			if(h[p] == n) return c[p];
		nt[++ tot] = fi[y], h[tot] = n, fi[y] = tot;
		return c[tot];
	}
	int find(int n) {
		int y = n % M;
		for(int p = fi[y]; p; p = nt[p])
			if(h[p] == n) return c[p];
		return 0;
	}
} f, rt;

#define i0 t[i].l
#define i1 t[i].r
struct tree {
	int l, r, x;
} t[N]; int tt;
int pl, pr, px;
void add(int &i, int x, int y) {
	if(y < pl || x > pr) return;
	if(!i) i = ++ tt;
	t[i].x ^= px;
	if(x == y) return;
	int m = x + y >> 1;
	add(i0, x, m); add(i1, m + 1, y);
}
void ft(int i, int x, int y) {
	if(!i || y < pl || x > pr) return;
	if(x >= pl && y <= pr) { px ^= t[i].x; return;}
	int m = x + y >> 1;
	ft(i0, x, m); ft(i1, m + 1, y);
}

int n, q, k, k2;

int op, x, y;

int w[32][200005], w0[200005];

void build() {
	ff(i, 1, k) {
		int t = calc(i);
		if(t >= k2) {
			w0[i] = t > k2 ? w0[i / 2] : i;
			w[0][i * 2 % k] = w0[i];
		}
	}
	fo(j, 1, 31) ff(i, 1, k)
		w[j][i] = w[j - 1][w[j - 1][i]];
}

int low(int x) {
	int s = 1;
	while(x % k == 0) s *= k, x /= k;
	return x % k * s;
}

int low2(int x) {
	int s = 1;
	while(x % k == 0) s *= k, x /= k;
	return x % k;
}

int qv(int x) {
	int s = 1;
	while(x % k == 0) s *= k, x /= k;
	return s;
}

void add(int x, int y) {
	while(x <= n) {
		if(calc(low2(x)) >= k2) break;
		f[x] ^= y;
		x += low(x);
	}
	if(x > n) return;
//	pp("%d %d\n", x, y);
	int l = low(x);
	int v = qv(x);
	int s = (x - l) / v / k;
	int z = w0[l / v];
	fd(i, 31, 0) if(s >> i & 1) z = w[i][z];
	pl = pr = x; px = y;
	add(rt[z * v], 1, n);
}

int sum(int x) {
	int ans = 0;
	for(; x; ) {
		ans ^= f.find(x);
		
		int l = low(x);
		if(calc(low2(x)) >= k2) {
			int v = qv(x);
			int s = (x - l) / v / k;
			int z = w0[l / v];
			fd(i, 31, 0) if(s >> i & 1) z = w[i][z];
			pl = 1, pr = x, px = 0;
			ft(rt.find(z * v), 1, n);
			ans ^= px;
		}
		
		x -= l;
	}
	return ans;
}

int main() {
	scanf("%d %d %d", &n, &q, &k);
	k2 = calc(k);
	build();
	fo(i, 1, q) {
		scanf("%d %d", &op, &x);
		if(op == 1) {
			scanf("%d", &y);
			add(x, y);
		} else {
			pp("%d\n", sum(x));
		}
	}
}

おすすめ

転載: www.cnblogs.com/coldchair/p/12669041.html