首页 > 其他分享 >P4115 Qtree4 题解

P4115 Qtree4 题解

时间:2023-11-17 18:34:39浏览次数:46  
标签:P4115 tx ers int 题解 top Qtree4 fa push

P4115

看到单点修改,求全局白色的最远距离,可以使用点分树。

考虑维护这棵点分树,想想树的直径的 dp 求法:\(f_u = \max\{f_v + w(u, v)\}\),答案为 \(\max(f_v+f_{v'})(v,v'\in \{\text{son}_u\})\),\(\{\text{son}_i\}\) 表示 \(i\) 的孩子集合。

这时候维护就十分简单了,对于每个点都记录两个可重集,分别表示 \(u\) 的儿子的子树的最大深度和 \(u\) 的子树内所有点到 \(fa_u\) 的距离,\(fa_u\) 表示 \(u\) 在点分树上的父亲节点,分别记这两个可重集为 \(A_i,B_i\),则 \(A_i = \{x|x=\max(B_v)\}\)。每次直接进行更新即可。最后还需记录一个可重集表示 \(\{f_i\}\)。

但是 multiset 速度太慢,有一个小技巧,就是记录两个堆,一个是原集合,一个是懒惰删除的堆,这样速度可以快很多。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vi vector < int >
#define eb emplace_back
#define pii pair < int, int >
#define fi first
#define se second
#define TIME 1e3 * clock() / CLOCKS_PER_SEC
bool Mbe;
mt19937_64 rng(35);
constexpr int N = 1e5 + 10;
struct Heap {
	priority_queue < int > pq, del;
	void push(int x) {
		pq.push(x);
	}
	void ers(int x) {
		del.push(x);
	}
	int top() {
		while(!del.empty() && pq.top() == del.top()) {
			pq.pop();
			del.pop();
		}
		return pq.top();
	}
	void pop() {
		while(!del.empty() && pq.top() == del.top()) {
			pq.pop();
			del.pop();
		}
		pq.pop();
	}
	int sstop() {
		int tmp = top();
		pop();
		int res = tmp + top();
		push(tmp);
		return res;
	}
	int size() {
		return pq.size() - del.size();
	}
};
struct edge {
	int to, w, nxt;
} e[N << 1];
int n, q, rt, dn;
int dep[N];
int a[N], mx[N], sz[N], vis[N], dfn[N], mi[20][N], fa[N];
int head[N], cnt_e;
void adde(int u, int v, int w) {
	++cnt_e, e[cnt_e].to = v, e[cnt_e].w = w, e[cnt_e].nxt = head[u], head[u] = cnt_e;
}
void dfs(int u, int ff) {
	mi[0][dfn[u] = ++dn] = ff;
	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(v == ff) continue;
		dep[v] = dep[u] + e[i].w;
		dfs(v, u);
	}
}
int get(int u, int v) {
	return dfn[u] < dfn[v] ? u : v;
}
int lca(int u, int v) {
	if(u == v) return u;
	if((u = dfn[u]) > (v = dfn[v])) swap(u, v);
	int d = __lg(v - u++);
	return get(mi[d][u], mi[d][v - (1 << d) + 1]);
}
int dis(int u, int v) {
	return dep[u] + dep[v] - 2 * dep[lca(u, v)];
}
void findroot(int u, int ff, int num) {
	sz[u] = 1, mx[u] = 0;
	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(v == ff || vis[v]) continue;
		findroot(v, u, num);
		sz[u] += sz[v];
		mx[u] = max(mx[u], sz[v]);
	}
	mx[u] = max(mx[u], num - sz[u]);
	if(mx[u] < mx[rt]) rt = u;
}
Heap Ans, tx[N], di[N]; // 答案; 所有儿子的子树的最大深度; 子树内所有点到其父亲的距离.
void getdep(int u, int fath, int anc) {
	di[anc].push(dis(u, fa[anc]));
	sz[u] = 1;
	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(v == fath || vis[v]) continue;
		getdep(v, u, anc);
		sz[u] += sz[v];
	}
}
void divide(int u) {
	vis[u] = 1;
	tx[u].push(0);
	getdep(u, 0, u);
	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(vis[v]) continue;
		rt = 0;
		findroot(v, u, sz[v]);
		fa[rt] = u;
		int tmp = rt;
		divide(rt);
		tx[u].push(di[tmp].top());
	}
	if(tx[u].size() >= 2) Ans.push(tx[u].sstop());
}
void ers(int u) {
	if(tx[u].size() >= 2) Ans.ers(tx[u].sstop());
}
void push(int u) {
	if(tx[u].size() >= 2) Ans.push(tx[u].sstop());
}
void update0(int u) {
	ers(u);
	tx[u].push(0);
	push(u);
	for(int x = u; fa[x]; x = fa[x]) {
		ers(fa[x]);
		if(di[x].size()) tx[fa[x]].ers(di[x].top());
		di[x].push(dis(u, fa[x]));
		tx[fa[x]].push(di[x].top());
		push(fa[x]);
	}
}
void update1(int u) {
	ers(u);
	tx[u].ers(0);
	push(u);
	for(int x = u; fa[x]; x = fa[x]) {
		ers(fa[x]);
		tx[fa[x]].ers(di[x].top());
		di[x].ers(dis(u, fa[x]));
		if(di[x].size()) tx[fa[x]].push(di[x].top());
		push(fa[x]);
	}
}
bool Med;
int main() {
	fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
//	freopen("data.in", "r", stdin);
//	freopen("myans.out", "w", stdout);
	ios :: sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> n;
	for(int i = 1; i < n; ++i) {
		int u, v, w;
		cin >> u >> v >> w;
		adde(u, v, w);
		adde(v, u, w);
	}
	dfs(1, 0);
	for(int i = 1; i <= __lg(n); ++i)
		for(int j = 1; j + (1 << i) - 1 <= n; ++j)
			mi[i][j] = get(mi[i - 1][j], mi[i - 1][j + (1 << i - 1)]);
	mx[0] = N;
	findroot(1, 0, n);
	divide(rt);
	cin >> q;
	for(int qi = 1, tot = n; qi <= q; ++qi) {
		char opt;
		cin >> opt;
		if(opt == 'C') {
			int x;
			cin >> x;
			a[x] ^= 1;
			if(a[x]) update1(x), --tot;
			else update0(x), ++tot;
		} else {
			if(!tot) cout << "They have disappeared.\n";
			else if(tot == 1) cout << 0 << "\n";
			else cout << max(Ans.top(), 0) << "\n";
		}
	}
	cerr << TIME << "ms\n";
	return 0;
}

标签:P4115,tx,ers,int,题解,top,Qtree4,fa,push
From: https://www.cnblogs.com/Pengzt/p/17839453.html

相关文章

  • 电脑网站支付报错“验签出错,建议检查签名字符串或私钥与应用公钥是否匹配”问题解决记
    在对接支付宝电脑网站支付的时候,遇到如下报错:“错误代码invalid-signature错误原因:验签出错,建议检查签名字符串或签名私钥与应用公钥是否匹配”。但展示的报错内容跟实际原因有所出入(在下文中有解答),这里记录下问题的解决排查过程。问题复现在对接电脑网站支付时,生成form表单......
  • 【C++中cin在Qt输出终端无法手动输入问题解决办法(详细)】
    现象:在Qt中使用cin进行对一个变量z进行输入,然后在用cout对z进行输出,结果没有进行手动输入,程序自动凭空出现类似512,32759等一些数值输出。 解决办法:第一步:在Qt左侧项目栏,在.pro文件中添加一行代码CONFIG+=console 第二步:在项目--运行--勾选在终端中运行(Runinterminal) 配置......
  • 苏格拉底问答、实践过程截图、遇到问题解决问题截图,代码链接
    #include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<semaphore.h>#include<pthread.h>#definemsleep(x)usleep(x*1000)#definePRODUCT_SPEED3//生产速度#defineCONSUM_SPEED1......
  • UVA 11178 Morley's Theorem 题解
    计算几何LinkUVA11178Morley'sTheoremQuestionMorley定理是这样的,作三角形ABC每个内角的三等分线,相交成三角形DEF,则DEF是等边三角形给出\(A,B,C\)坐标,求\(D,E,F\)坐标Solution其实是一道计算几何板子题只需要计算\(\angleABC\)的值\(a\),然后把\(BC\)逆......
  • 【题解 CF1628D2】 Game on Sum
    GameonSum(HardVersion)题面翻译Alice和Bob正在玩一个游戏,游戏分为\(n\)个回合,Alice和Bob要轮流对一个数\(x\)进行操作,已知这个数初始值是\(0\)。具体每个回合的行动规则如下:Alice选择一个在区间\([0,k]\)之间的实数\(t\)。Bob可以选择让\(x\)变成\(......
  • A2OJ Ladder 32 简要题解
    https://earthshakira.github.io/a2oj-clientside/server/Ladder32.html只记录Difficultylevel>=8的。有很多题是口胡的。写了的会标注提交记录。还有些很久以前写过的题就懒得搬提交记录了。任何的*都表示该段的后续部分未能想出并查看了题解。159.CF372DChoosingSub......
  • 【题解 ABC180F】 Unbranched
    [ABC180F]Unbranched题面翻译求\(N\)个点,\(M\)条边且满足以下条件的图的数量:图中无自环;每个点度数最多为\(2\);连通块大小的最大值恰好为\(L\)。答案对\(10^9+7\)取模。\(2\leN\le300\),\(1\leM,L\leN\)。题目描述頂点にラベルが付き辺にはラベルが付い......
  • 题解 P7972【[KSN2021] Self Permutation】
    怎么其他两篇题解都是\(O(n\logn)\)的,来发一个\(O(n)\)做法,当考前复习了。对原序列建出小根笛卡尔树,节点编号与原序列中的下标相同。记\(T_u\)表示以\(u\)为根的子树,\(lc(u),rc(u)\)分别表示\(u\)的左儿子和右儿子。设\(f_u\)表示删除若干\(T_u\)中的点(可以不删......
  • [ARC106F] Figures 题解
    题意给定\(N\)个带有若干洞的节点,其中第\(i\)个点上有\(d_i\)个洞。先可以在两个不同的节点的洞之间连边,一个洞最多连一条边,求使得最终形成的图是一棵树的方案数,对\(998244353\)取模。洞之间相互区分,两个方案不同当且仅当存在一条边在两个方案中的连的洞不同。\(2\l......
  • P9400 题解
    blog。很naive的题,写这篇题解,主要是现有题解都用的线段树/平衡树,让我感到很难绷。一眼DP。\(dp_{i,j}\)表示前\(i\)个宿舍,现在有连续\(j\)个灯亮大于\(B\),方案数。\(dp_{i,0}=\max(\min(B,r_i)-l_i+1,0)\cdot\sum\limits_{j=0}^{A-1}dp_{i-1,j}\)。\(dp_{i......