记录美好生活 艹

Red Black Tree

 磨磨蹭蹭地写虚树,结果半天没出来。大佬说 二分 求公共节点的lca,一下就出来了
 二分就是取那些要变更的点的lca 然后判断这样log^2,好像也可以排序差分区间和弄到log,虚树就是暴力枚举然后换根dp,没弄好debug一下午。




虚树

// 基本没啥用就 两个函数是要想的,其他都是虚树构造必须
 虚树构造相关操作
void dfs(int u, int f);
int lca(int u, int v);
LL build(int n);
 需要思考的操作
LL dfs(int u);  // 处理 ma ,K 和 L;
LL df(int u, LL k); 




int h[N], ne[N<<1], e[N<<1], w[N<<1], idx;
void add(int a, int b, int c){e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;return ;}

int dfn[N], cv, fa[N][20], deep[N]; 
LL mi[N], ma[N], K[N], L[N], de[N]; 
// mi[i] i点的值, ma[i] i为根的子树的值的最大值;
// K[i] 以i为根的子树 到i的路径上有红色点的值的最大值,L[i] 以i为根的子树 到 i的路径上没有红色点的值的最大值
// de[i] i 到 整棵树的根节点的路径和, 用来判断 两点之间是否有红色点;
bool c[N];	// = 1 代表是红色点 

void dfs(int u, int f) // 预处理 mi lca 
{
	dfn[u] = cv ++; deep[u] = deep[f] + 1;
	for(int i = 1;i < 20;i ++) fa[u][i] = fa[fa[u][i-1]][i-1];
	for(int i = h[u];~i ;i = ne[i])
	{
		if(e[i] == f)continue;
		fa[e[i]][0] = u; 
		mi[e[i]] = c[e[i]] ? 0 : mi[u] + w[i]; 
		de[e[i]] = de[u] + w[i];
		dfs(e[i], u);
	}
}

int lca(int u, int v)
{
	if(deep[u]<deep[v])swap(u, v);
	int x = deep[u] - deep[v];
	for(int i = 0;i < 20;i ++)if(x>>i&1)u = fa[u][i];
	if(u == v)return v;
	for(int i = 19;i >= 0;i --)
		if(fa[u][i] != fa[v][i])
			u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}
int a[N];
bool dis[N];
bool cmp(int a,int b){return dfn[a]<dfn[b];}

bool in(int u, int v){return !c[u] && mi[v] - mi[u] == de[v] - de[u];} // 判断 u -> v 是否有红色节点。
LL dfs(int u)  // 处理 ma ,K 和 L;
{
	ma[u] = K[u] = L[u] = 0;
	if(dis[u]) ma[u] = mi[u], (c[u] ? K[u] = mi[u] : L[u] = mi[u]);   
	for(int i = h[u]; ~i;i = ne[i])
	{
		ma[u] = max(ma[u], dfs(e[i]));	
		K[u] = max(K[u], K[e[i]]);
		in(u, e[i]) ?  L[u] = max(L[u], L[e[i]]) : K[u] = max(K[u], L[e[i]]);
	}
	return ma[u];
}

// 处理答案 k 是 以u为根的树之外的 节点的值 的 最大值;
// 那么 把 u 作为 红色节点的答案 = max(k, max(K[u], L[u]-mi[i]));
LL df(int u, LL k) 
{
	vector<LL> v, m; 
	for(int i = h[u]; ~i;i = ne[i])
		v.push_back(ma[e[i]]), m.push_back(ma[e[i]]);
	for(int i = 1;i < v.size();i ++) v[i] = max(v[i], v[i-1]);
	for(int i = m.size()-2;i >= 0;i --) m[i] = max(m[i], m[i+1]); // 处理这棵树的前缀最大值和后缀最大值。
	int now = 0;
	LL ans = max(k, max(K[u], L[u]-mi[u]));
	for(int i = h[u]; ~i;i = ne[i])
	{
		LL tem = k;
		if(dis[u]) tem = max(tem, mi[u]);
		if(now != v.size()-1) tem = max(tem, m[now+1]);
		if(now) tem = max(tem, v[now-1]); 
		ans = min(ans, df(e[i], tem)); 
		++ now;
	}
	h[u] = -1;
	return ans;
}
stack<int> st;
LL build(int n) // 建立虚树 
{
	st.push(1); idx = 0;
	int tmp;
	for(int i = 1;i <= n;i ++)
	{
		dis[a[i]] = 1;
		if(a[i] == 1) continue;
		int lc = lca(a[i], st.top());
		while(lc != st.top())
		{
			tmp = st.top(); st.pop();
			if(dfn[st.top()] < dfn[lc]) st.push(lc);
			add(st.top(), tmp, 0);
		}
		st.push(a[i]);
	}
	while(st.size() > 1)
	{
		tmp = st.top(); st.pop();
		add(st.top(), tmp, 0);
	}
	st.pop();
	dfs(1);
	return df(1, 0);
}

int main() 
{
	int t;
	scanf("%d", &t);
	while(t --)
	{
		int n, m, q;
		scanf("%d%d%d", &n, &m, &q);
		for(int i = 1;i <= n;i ++) h[i] = -1; idx = 0; cv = 0;
		for(int i = 1;i <= m;i ++)
		{
			int x;
			scanf("%d", &x);
			c[x] = 1;
		}
		for(int i = 2;i <= n;i ++)
		{
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			add(u, v, w); add(v, u, w);
		}
		dfs(1, 0);
		for(int i = 1;i <= n;i ++) h[i] = -1;
		for(int _ = 1;_ <= q;_ ++)
		{
			int k;
			scanf("%d", &k);
			for(int i = 1;i <= k;i ++) scanf("%d", a+i);
			sort(a+1, a+k+1, cmp);
			printf("%lld\n", build(k));
			for(int i = 1;i <= k;i ++) dis[a[i]] = 0;
		}
		for(int i = 1;i <= n;i ++) c[i] = 0;
	}
	return 0;
}

二分

int h[N], ne[N<<1], e[N<<1], w[N<<1], idx;
void add(int a, int b, int c){e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;return ;}

int fa[N][20], deep[N], a[N]; 
LL mi[N], de[N]; 
bool c[N];

void dfs(int u, int f)
{
	deep[u] = deep[f] + 1;
	for(int i = 1;i < 20;i ++) fa[u][i] = fa[fa[u][i-1]][i-1];
	for(int i = h[u];~i ;i = ne[i])
	{
		if(e[i] == f)continue;
		fa[e[i]][0] = u; 
		mi[e[i]] = c[e[i]] ? 0 : mi[u] + w[i]; 
		de[e[i]] = de[u] + w[i];
		dfs(e[i], u);
	}
}
int lca(int u, int v)
{
	if(deep[u]<deep[v])swap(u, v);
	int x = deep[u] - deep[v];
	for(int i = 0;i < 20;i ++)if(x>>i&1)u = fa[u][i];
	if(u == v)return v;
	for(int i = 19;i >= 0;i --)
		if(fa[u][i] != fa[v][i])
			u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}


bool cmp(int u, int v){return mi[u] < mi[v];}
bool in(int u, int v){return !c[u] && mi[v]-mi[u] == de[v] - de[u];} // 判断 u -> v 是否有红色节点。
bool ch(LL t, int n)
{
	int now = 0;
	for(int i = 1;i <= n;i ++)
		if(mi[a[i]] > t) now = now ? lca(now, a[i]) : a[i];
	for(int i = 1;i <= n;i ++)
		if(mi[a[i]] > t)
			if(!in(now, a[i]) || mi[a[i]] - mi[now] > t)
				return 0;
	return 1;
}

int main() 
{
	int t;
	scanf("%d", &t);
	while(t --)
	{
		int n, m, q;
		scanf("%d%d%d", &n, &m, &q);
		for(int i = 1;i <= n;i ++) h[i] = -1; idx = 0; 
		for(int i = 1;i <= m;i ++)
		{
			int x;
			scanf("%d", &x);
			c[x] = 1;
		}
		for(int i = 2;i <= n;i ++)
		{
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			add(u, v, w); add(v, u, w);
		}
		dfs(1, 0);
		for(int _ = 1;_ <= q;_ ++)
		{
			int k;
			scanf("%d", &k);
			for(int i = 1;i <= k;i ++) scanf("%d", a+i);
			sort(a+1, a+k+1, cmp);
			LL l = 0, r = 1e17;
			while(l < r)
				ch(mid, k) ? r = mid : l = mid+1;
			printf("%lld\n", l);
		}
		for(int i = 1;i <= n;i ++) c[i] = 0;
	}
	return 0;
}

你可能感兴趣的:(动态规划,算法,哈希算法)