P7215 [JOISC2020] 首都

前言

这道题是老师拉到点分治题单中的,可是我却一眼就出了另一个做法。

思路

首先对于这道题可以发现如果一个颜色如果仅需要依靠另一种颜色而一次这样下去最后一个颜色也仅需要第一个颜色,那么这样的话就能只将这些合并起来即可。所以我们可以将这种状态转移成建边的形式,如果对于两种颜色 \(x\)\(y\) 如果 \(x\) 依靠 \(y\) 则连一条边 \(x\to y\),然后只需要找一个包含颜色且环中颜色数量最少的即可。

  • 我们发现对于连边操作,是一个点向一段区间连边所以可以用树链剖分,然后从 \(i\)\(l\sim r\) 连边即可,这里其实可以用线段树优化建边解决。
  • 对于同一个颜色中,可以发现我们需要把每两个点经过的路径都被 \(i\) 种颜色连边,但是这样暴力的话是 \(n^2\) 的所以我们可以对于每一个点的 dfs 序进行排序然后只需要对于两两之间连边即可。

代码

#include <bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include <ext/rope>
using namespace __gnu_pbds;
using namespace std;
#define int long long
#define pb push_back
#define rep(i,x,y) for(register int i=x;i<=y;i++)
#define rep1(i,x,y) for(register int i=x;i>=y;--i)
#define in(x) scanf("%lld",&x)
#define fire signed
#define il inline
il void print(int x) {
	if(x<0) putchar('-'),x=-x;
	if(x>=10) print(x/10);
	putchar(x%10+'0');
}
int T=1;
int n,k;
const int N=2e5+10,M=1e6+10;
vector<int>v[M],ve[M];
int c[M],dfn[M],low[M],idx,val[M],chu[M];
stack<int>s;
int sum;
int is[M],tot,siz1[M];
void tarjan(int x) {
	dfn[x]=low[x]=++idx;
	s.push(x);
	is[x]=1;
	for(auto to:ve[x]) {
		if(!dfn[to]) {
			tarjan(to);
			low[x]=min(low[x],low[to]);
		}else if(is[to]) low[x]=min(low[x],dfn[to]);
	}
	if(low[x]==dfn[x]) {
		int p;
		tot++;
		do{
			p=s.top();
			s.pop();
			is[p]=false;
			val[p]=tot;
			siz1[tot]+=(p<=k);
		}while(p!=x);
	}
}
int dui[M];
int son[M],siz[M],dep[M],fa[M],top[M];
void dfs(int x,int f) {
	siz[x]=1;
	fa[x]=f;
	dep[x]=dep[f]+1;
	int Max=false;
	for(auto to:v[x]) {
		if(to==f) continue;
		dfs(to,x);
		if(siz[to]>Max) {
			Max=siz[to];
			son[x]=to;
		}
		siz[x]+=siz[to];
	}
}
int dfn1[N],cnt;
void dfs1(int x,int h) {
	top[x]=h;
	dfn1[x]=++cnt;
	dui[cnt]=c[x];
	if(!son[x]) return ;
	dfs1(son[x],h);
	for(auto to:v[x]) if(!dfn1[to]) dfs1(to,to);
}
struct node{
	int l,r;
}tr[M];
void add(int x,int y) {
	sum=max({sum,x,y});
	ve[x].push_back(y);
}
vector<int>co[N];
void build(int u,int l,int r) {
	tr[u]={l,r};
	if(l==r) {
		add(u+k,dui[l]);
		return ;
	}
	int mid=l+r>>1;
	add(u+k,2*u+k);
	add(u+k,u*2+1+k);
	build(u<<1,l,mid);
	build(u<<1|1,mid+1,r);
}
void modify(int u,int l,int r,int k1) {
	if(tr[u].l>=l&&tr[u].r<=r) {
		add(k1,u+k);
		return ;
	} 
	int mid=tr[u].l+tr[u].r>>1;
	if(mid>=l) modify(u<<1,l,r,k1);
	if(mid<r) modify(u<<1|1,l,r,k1);
}
int arr[M];
bool cmp(int a,int b) {
	return dfn1[a]<dfn1[b];
}
void get(int x,int y,int k1) {
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		modify(1,dfn1[top[x]],dfn1[x],k1);
		x=fa[top[x]];
	}
	if(dfn1[x]>dfn1[y]) swap(x,y);
	modify(1,dfn1[x],dfn1[y],k1);
}
void solve() {
	in(n),in(k);
	rep(i,1,n-1) {
		int x,y;
		in(x),in(y);
		v[x].push_back(y);
		v[y].push_back(x);
	}
	rep(i,1,n) in(c[i]),co[c[i]].push_back(i);
	dfs(1,0);
	dfs1(1,1);
	build(1,1,n);
	rep(i,1,k) {
		int cc=false;
		for(auto to:co[i]) arr[++cc]=to;
		sort(arr+1,arr+1+cc,cmp);
		rep(j,1,cc-1) get(arr[j],arr[j+1],i);
	}
	rep(i,1,sum) if(!dfn[i]) tarjan(i);
	rep(i,1,sum) for(auto to:ve[i]) if(val[i]!=val[to]){
		chu[val[i]]++;
		break;
	} 
	int res=LONG_LONG_MAX;
	rep(i,1,tot) if(!chu[i]&&siz1[i]!=0) res=min(res,siz1[i]-1);
	print(res);
	return;
}
fire main() {
	while(T--) {
		solve();
	}
	return false;
}
posted @ 2024-05-23 19:06  highkj  阅读(5)  评论(0)    收藏  举报