1. 程式人生 > 其它 >LOJ3040 「JOISC 2019 Day4」合併

LOJ3040 「JOISC 2019 Day4」合併

這道題目需要我們 \(\text{check}\) 每一棵子樹內是否存在完全位於該子樹內的顏色。

這個我們可以利用 \(\text{dfn}\) 來實現。

具體的就是找到每一個顏色在 \(\text{dfn}\) 上的第一個出現位置和最後一個出現位置,每一次將當前子樹的 \(\text{dfn}\) 區間與子樹內所有顏色的這個第一次出現位置和最後一個出現位置比較即可。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,k,a[N];
struct Edge{int nxt,to;}e[N<<1];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
struct DSU{
	int fa[N];
	void init(){for(int i=1;i<=n;++i)fa[i]=i;}
	int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
	void merge(int u,int v){
		int fu=find(u),fv=find(v);
		if(fu!=fv) fa[fu]=fv;
	}
}d;
int L[N],R[N],mx[N],mn[N],cnt_dfn=0;
void dfs1(int u,int fa){
	L[u]=++cnt_dfn;
	mn[a[u]]=min(mn[a[u]],cnt_dfn);
	mx[a[u]]=max(mx[a[u]],cnt_dfn);
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v!=fa) dfs1(v,u);
	}
	R[u]=cnt_dfn;
}
int LL[N],RR[N];
void dfs2(int u,int fa){
	LL[u]=mn[a[u]],RR[u]=mx[a[u]];
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa) continue;
		dfs2(v,u),LL[u]=min(LL[u],LL[v]),RR[u]=max(RR[u],RR[v]);
	}
	if(LL[u]<L[u]||R[u]<RR[u]) d.merge(u,fa);
}
int deg[N],res=0;
int main(){
	cin>>n>>k;
	for(int i=1;i<n;++i){
		int u,v;scanf("%d%d",&u,&v);
		add(u,v,i<<1),add(v,u,i<<1|1);
	}
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	for(int i=1;i<=k;++i) mn[i]=1e9+7;
	d.init(),dfs1(1,0),dfs2(1,0);
	for(int i=1;i<n;++i){
		if(d.find(e[i<<1].to)!=d.find(e[i<<1|1].to))
		deg[d.find(e[i<<1].to)]++,deg[d.find(e[i<<1|1].to)]++;
	}
	for(int i=1;i<=n;++i) if(d.find(i)==i) res+=(deg[i]==1);
	return printf("%d\n",(res+1)/2),0;
}
/*
我們相當於是列舉每一條邊,該邊的權值就是兩邊子樹完全位於其中的州的個數取 $\max$ 。
然後我們如何數出完全位於該子樹中的州的個數呢?一個子樹對應一個 $\text{dfn}$ 區間,
而一個州有著位於 $\text{dfn}$ 上最前和最後的兩個點,如果這兩個點的 $\text{dfn}$
都位於這個子樹的區間中,那麼必然,這整個州都位於 $\text{dfn}$ 中,這就相當於一個
二維數點。那麼完全不位於其中的個數呢?

好像我們可以考慮離線下來區間數顏色,複雜度是一個根號的。有夢想可以搞。

我們這是一棵樹啊,子樹數顏色不是直接線段樹合併嘛?然後就很簡單了,對於子樹內的,用
$dfn$ 求,對於子樹外的,用數顏色容斥一下。

好屎啊。
--------------------------------------------------------------------------
等一下,為啥提交記錄裡的程式碼都這麼短啊,我是不是假了啊。

哈哈,題意完全轉換錯誤。

我們就這樣考慮,判斷每一條邊是否能被拆分。對於能被拆分的邊我們保留,不然合併,我們
就可以得到另一個樹,然後我們需要用最少的路徑去覆蓋整一棵樹,直接葉子個數除以二上取
整即可。

相當於,對於一條邊,我們只需要判斷其兩端是否存在獨立的州,如果有,就保留,不然將邊
兩端的點合併,然後對於新圖,數一下葉子就行了。
*/