1. 程式人生 > 實用技巧 >[Contest on 2020.12.1] ZZH的遊戲

[Contest on 2020.12.1] ZZH的遊戲

\(\text{Description}\)

傳送門

\(\text{Solution}\)

可以先考慮二分一個答案,發現可以先固定一個端點,另一個點拓展與它相連通的小於等於那個點的點,然後更新另一個點連通塊的最小值,再用先開始的端點拓展…當無法拓展且需要新加入的點與另一邊的最小值點相加大於二分的答案,而且兩邊的 \(1\) 都沒被包含就不合法,這個過程可以用堆來維護,時間複雜度 \(\mathcal O(n\log ^2n)\)

其實可以省掉二分,我們把初始答案設為 \(s+t\),按上面的方法直接拓展即可。時間複雜度 \(\mathcal O(n\log n)\)

其實也可以省掉堆,因為這個題目有一個性質:權值是連續的 \([1,n]\)

區間內的數。

對於每一個點我們有兩個標記:\(vis,in\),分別表示旁邊是否已經有進塊的點,是否在連通塊。一邊找到最小值後,其實不用直接賦值,可以將最小值線一步步下挪,將另一邊的最大值線一步步上挪,嘗試拓展最大值線代表的點入塊,這樣就是 \(\mathcal O(n)\) 的?

\(\text{Code}\)

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}

const int maxn=1e6+5;

int n,lim[2],ans,s,t,lims,limt,mins,mint;
bool vis[maxn][2],In[maxn][2];
int cnt[2],head[maxn][2],nxt[maxn<<1][2],to[maxn<<1][2];

void addEdge(int u,int v,bool op) {
	nxt[++cnt[op]][op]=head[u][op],to[cnt[op]][op]=v,head[u][op]=cnt[op];
	nxt[++cnt[op]][op]=head[v][op],to[cnt[op]][op]=u,head[v][op]=cnt[op];
}

void dfss(int u) {
	In[u][0]=1;
	if(u<mins) mins=u;
	for(int i=head[u][0];i;i=nxt[i][0]) {
		int v=to[i][0];
		if(!In[v][0]) {
			if(lims<v) vis[v][0]=1;
			else dfss(v);
		}
	}
}

void dfst(int u) {
	In[u][1]=1;
	if(u<mint) mint=u;
	for(int i=head[u][1];i;i=nxt[i][1]) {
		int v=to[i][1];
		if(!In[v][1]) {
			if(limt<v) vis[v][1]=1;
			else dfst(v);
		}
	}
}

int main() {
	int u,v;
	for(int T=read(9);T;--T) {
		n=read(9); cnt[0]=cnt[1]=0;
		for(int i=1;i<=n;++i) head[i][0]=head[i][1]=In[i][0]=In[i][1]=vis[i][0]=vis[i][1]=0;
		for(int i=1;i<n;++i) u=read(9),v=read(9),addEdge(u,v,0);
		for(int i=1;i<n;++i) u=read(9),v=read(9),addEdge(u,v,1);
		s=read(9),t=read(9); lims=mins=s,mint=limt=t;
		vis[s][0]=vis[t][1]=1; ans=s+t;
		while(233) {
			if(lims<=n&&vis[lims][0]&&(!In[lims][0])) dfss(lims);
			if(limt<=n&&vis[limt][1]&&(!In[limt][1])) dfst(limt);
			if(s==mins&&t==mint) break;
			if(s>mins) --s,++limt;
			if(t>mint) --t,++lims;
		}
		while(!In[1][0]||In[1][1]==0) {
			++ans; ++lims,++limt;
			while(233) {
				if(lims<=n&&vis[lims][0]&&(!In[lims][0])) dfss(lims);
				if(limt<=n&&vis[limt][1]&&(!In[limt][1])) dfst(limt);
				if(s==mins&&t==mint) break;
				if(s>mins) --s,++limt;
				if(t>mint) --t,++lims;
			}
		}
		print(ans,'\n');
	}
	return 0;
}