P5891 Fracture Ray

发布时间 2023-09-25 10:43:43作者: Ender_32k

2D0y a。

结论拍脸。

显然如果 \(i\to i+\text{popcount(i)}\) 这样连边的话,连出来是一个森林。

结论就是 \(q\)\(u\) 到根的路径的点,去重后的个数不超过 \(8\times 10^7\)

然后 bitset 维护所有走过的点,建出虚树,点数就变成 \(O(q)\) 的了。

然后树剖线段树就能在 \(O(q\log^2 q)\) 的时间内解决。

要手写 bitset

// Problem: P5891 Fracture Ray
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5891
// Memory Limit: 500 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define mt make_tuple
#define mp make_pair
#define fi first
#define se second
// #define FILE

using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
typedef tuple<int, int, int> tu;
bool Med;

const int N = 5e6 + 500;
const int M = 3.5e7 + 350;

map<int, int> pid;
vector<tu> qr;
vector<int> p, g[N];
int q, m, rt, tot, dfc;
int fa[N], sz[N], dep[N], son[N], id[N], top[N];
ll s[N], vl[N];

struct BIT {
	unsigned int f[M];
	void reset() { memset(f, 0, sizeof(f)); }
	void set(int x) { f[x >> 5] |= (1ll << (x & 31)); }
	int test(int x) { return (f[x >> 5] >> (x & 31)) & 1; }
} B;

struct SEG {
	ll tr[N << 2], lz[N << 2];
	#define ls x << 1
	#define rs x << 1 | 1
	#define mid ((l + r) >> 1)
	void ptg(int l, int r, ll c, int x) { tr[x] += 1ll * (s[r] - s[l - 1]) * c, lz[x] += c; }
	void pdn(int l, int r, int x) {
		if (!lz[x]) return;
		ptg(l, mid, lz[x], ls), ptg(mid + 1, r, lz[x], rs);
		lz[x] = 0;
	}
	void upd(int l, int r, int s, int t, ll c, int x) {
		if (t < s) return;
		if (s <= l && r <= t) return ptg(l, r, c, x);
		pdn(l, r, x);
		if (s <= mid) upd(l, mid, s, t, c, ls);
		if (t > mid) upd(mid + 1, r, s, t, c, rs);
		tr[x] = tr[ls] + tr[rs];
	}
	ll qry(int l, int r, int s, int t, int x) {
		if (t < s) return 0;
		if (s <= l && r <= t) return tr[x];
		pdn(l, r, x); ll res = 0;
		if (s <= mid) res += qry(l, mid, s, t, ls);
		if (t > mid) res += qry(mid + 1, r, s, t, rs);
		return res;
	}
} T;

#define pct(x) __builtin_popcountll(x)
#define gt(x, y) get<y>(x)

int gid(int u) {
	if (!pid[u]) pid[u] = ++tot;
	return pid[u];
}

void dfs1(int u) {
	sz[u] = 1, dep[u] = dep[fa[u]] + 1;
	for (int v : g[u]) {
		dfs1(v), sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) son[u] = v;
	}
}

void dfs2(int u, int pr) {
	top[u] = pr, s[id[u] = ++dfc] = vl[u];
	if (son[u]) dfs2(son[u], pr);
	for (int v : g[u]) {
		if (v == son[u]) continue;
		dfs2(v, v);
	}
}

void upd(int u, int c) {
	while (u) {
		T.upd(1, dfc, id[top[u]], id[u], c, 1);
		u = fa[top[u]];
	}
}

ll qry(int u) {
	ll res = 0;
	while (u) {
		res += T.qry(1, dfc, id[top[u]], id[u], 1);
		u = fa[top[u]];
	}
	return res;
}

void solve() {
	cin >> q >> m;
	for (int i = 1, op, u, w; i <= q; i++) {
		cin >> op >> u;
		if (op == 1) cin >> w, qr.pb(mt(op, u, w));
		else qr.pb(mt(op, u, 0));
		p.pb(u);
		for (; u <= m && !B.test(u); u += pct(u)) B.set(u);
		if (u <= m) p.pb(u);
	}
	p.pb(m + 1);
	sort(p.begin(), p.end());
	p.erase(unique(p.begin(), p.end()), p.end());
	reverse(p.begin(), p.end()), B.reset();
	for (int u : p) {
		int iu = gid(u), ifa;
		if (u == m + 1) continue;
		for (; u <= m && !B.test(u); u += pct(u)) 
			vl[iu]++, B.set(u);
		if (u <= m) ifa = gid(u);
		else ifa = gid(m + 1);
		if (ifa == iu) ifa = gid(m + 1);
		g[ifa].pb(iu), fa[iu] = ifa;
	}
	rt = gid(m + 1);
	for (int i = 1; i <= tot; i++)
		if (i != rt && !fa[i])
			g[rt].pb(i), fa[i] = rt;
	dfs1(rt), dfs2(rt, rt);
	for (int i = 1; i <= dfc; i++) 
		s[i] += s[i - 1];
	for (tu p : qr) {
		if (gt(p, 0) == 1) upd(gid(gt(p, 1)), gt(p, 2));
		else cout << qry(gid(gt(p, 1))) << '\n';
	}
}

bool Mbe;
signed main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cerr << (&Med - &Mbe) / 1048576.0 << " MB\n";
	#ifdef FILE
		freopen("A.in", "r", stdin);
		freopen("B.out", "w", stdout);
	#endif
	int T = 1;
	// cin >> T;
	while (T--) solve();
	cerr << (int)(1e3 * clock() / CLOCKS_PER_SEC) << " ms\n";
	return 0;
}