P8290 [省選聯考 2022] 填樹

WrongAnswer_90發表於2024-04-16

My Blogs

P8290 [省選聯考 2022] 填樹

很有意思的拉插最佳化 DP。

首先可以列舉 \(L\) 來限制選的數的值域在 \(L,L+k\) 中。然後進行樹上 DP:設 \(v_i\) 表示當前點 \(i\) 能填多少種數,\(w_i\) 表示當前點 \(i\) 能填的數的和。

\(f_i\) 表示當前 \(i\) 子樹內的所有合法根鏈數量,\(g_i\) 表示 \(i\) 子樹內所有根鏈的權值之和。假設在合併子樹 \(to\),轉移方程:

\[\begin{aligned} v_i&=\max(0,\min(r_i,L+k)-\max(l_i,L)+1)\\ w_i&=\max(0,\frac{(\min(r_i,L+k)+\max(l_i,L))v_i}2)\\ ans1&\rightarrow ans1+f_if_{to}\\ ans2&\rightarrow ans2+f_ig_{to}+f_{to}g_i\\ f_i&\rightarrow f_i+v_if_{to}\\ g_i&\rightarrow g_i+v_ig_{to}+f_{to}w_i \end{aligned} \]

但是這樣會算重,比如一個最小值為 \(3\),最大值為 \(5\) 的方案,在 \(k=3\) 的時候會被算兩次(\(L=2,R=5\) 時被算一次,\(L=3,R=6\) 時被算一次)。可以在 DP 中多開一維記錄最小值是否取到了 \(L\),但是略顯繁瑣,常數也並不優秀。

所以考慮容斥,算 \(2V\) 遍,每次用 \((L,L+k)\) 的答案減去 \((L+1,L+k)\) 的答案,這樣得到的就是最小值恰好為 \(L\) 的答案。

一次 DP 複雜度是 \(\mathcal O(n)\),總複雜度 \(\mathcal O(nV)\)。但是還不夠。

觀察 \(ans1\)\(ans2\),如果用分段法把 \(\min\)\(\max\) 拆開,則他們在同一段下一定都是關於 \(V\) 的不超過 \(n+1\) 次的多項式。

對於 \(ans1\),其是由若干個 \(v\) 乘起來的,而 \(v\) 要麼是常數,要麼是關於 \(L\) 的單項式,所以次數不會超過 \(n\)

對於 \(ans2\),是由一個 \(w\) 和多個 \(v\) 乘起來的,而 \(w\) 可能是一個關於 \(L\) 的二次多項式,所以最高次數是 \(n+1\)。下面只考慮處理 \(ans2\)\(ans1\) 同理。

這樣考慮設一個連續段 \(l,r\),設 \(ans_i\)\(L=i+l-1\) 時的 \(ans2\)。有結論:把 \(ans_i\) 做一遍字首和,得到的 \(ans_i\) 是關於 \(L\) 的不超過 \(n+2\) 次的多項式。

考慮設 \(F(x)=\sum_{i=0}^na_ix^i\),則做一遍字首和之後 \(S'(x)=\sum_{i=0}^na_i\sum_{j=1}^xx^j\),最高次項是一個 \(n+1\) 次的自然數冪和,所以得到的字首和最高次不超過 \(n+2\)

所以可以只求 \(n+3\) 個點值,用拉插公式求出在 \(L=r\) 時字首和的點值。連續段個數是 \(\mathcal O(n)\) 的,每次需要做 \(\mathcal O(n)\) 次 DP(來確定前 \(n+3\) 個點值),每次 DP 複雜度是 \(\mathcal O(n)\),拉插複雜度不是瓶頸,總複雜度 \(\mathcal O(n^3)\)。注意常數最佳化。

另一種做法:可以考慮列舉了連續段之後不暴力拉插,而是 DP 的時候直接多項式。因為次數不會超過 \(siz_i\),所以暴力做複雜度是樹上揹包的 \(\mathcal O(n^2)\)。然後得到了一個 \(\mathcal O(n)\) 次的多項式,想辦法求出它的字首和應該也能做,但是感覺不如拉插簡潔和直接。

int n,m,ans1,ans2,sum1,sum2,cnt,len,L,R,v[210],v2[210],fr[210];
int a[210],b[210],f[210],g[210],head[210],to[210],nex[210],numa[810];
vi T[210];
inline void add(int x,int y){to[++cnt]=y,nex[cnt]=head[x],head[x]=cnt;}
void dfs0(int x,int fa){for(auto to:T[x])if(to!=fa)add(x,to),dfs0(to,x);}
void dfs(int x)
{
	int v1=0,v2=0;
	if(min(R,b[x])<max(L,a[x]))f[x]=g[x]=0;
	else
	f[x]=v1=max(0,min(R,b[x])-max(L,a[x])+1),
	g[x]=v2=Cmul(min(R,b[x])+max(L,a[x]),f[x],(MOD+1)>>1);
	Madd(sum1,f[x]),Madd(sum2,g[x]);
	for(int i=head[x];i;i=nex[i])
	{
		dfs(to[i]);
		if(!v1)continue;
		Madd(sum1,Cmul(f[x],f[to[i]])),Madd(sum2,Cmul(f[x],g[to[i]]),Cmul(g[x],f[to[i]]));
		Madd(g[x],Cadd(Cmul(f[to[i]],v2),Cmul(v1,g[to[i]]))),Madd(f[x],Cmul(v1,f[to[i]]));
	}
}
inline void mian()
{
	read(n,m),fr[0]=1;int x,y,rr=-inf;L=inf;
	for(int i=1;i<=204;++i)fr[i]=Cmul(fr[i-1],i);
	for(int i=1;i<=n;++i)read(a[i],b[i]),Mmin(L,a[i]),Mmax(rr,b[i]);
	for(int i=1;i<n;++i)read(x,y),T[x].eb(y),T[y].eb(x);
	dfs0(1,0),R=L+m-1;
	for(int i=1;i<=n;++i)numa[++len]=a[i],numa[++len]=b[i]-m+1,numa[++len]=b[i]+1,numa[++len]=a[i]-m;
	sort(numa+1,numa+1+len),len=unique(numa+1,numa+1+len)-numa-1;
	for(int i=1;i<len;++i)
	{
		if(numa[i+1]-numa[i]<=n+3)
		{
			L=numa[i],R=L+m;
			while(L<numa[i+1])sum1=sum2=0,dfs(1),Madd(ans1,sum1),Madd(ans2,sum2),++L,++R;
			continue;
		}
		for(int j=1;j<=n+3;++j)
		{
			L=numa[i]-1+j,R=numa[i]-1+j+m,sum1=sum2=0,dfs(1);
			v[j]=Cadd(v[j-1],sum1),v2[j]=Cadd(v2[j-1],sum2);
		}
		int tmp=1,X=numa[i+1]-1;
		for(int j=1;j<=n+3;++j)Mmul(tmp,X-(numa[i]+j-1));
		for(int j=1;j<=n+3;++j)
		{
			int va=fr[j-1];
			if((n+3-j)&1)Mmul(va,Cdel(0,fr[n+3-j]));
			else Mmul(va,fr[n+3-j]);
			Madd(ans1,Cmul(v[j],tmp,power(Cmul(va,X-(numa[i]+j-1)),MOD-2)));
			Madd(ans2,Cmul(v2[j],tmp,power(Cmul(va,X-(numa[i]+j-1)),MOD-2)));
		}
	}
	--m;
	for(int i=1;i<len;++i)
	{
		if(numa[i+1]-numa[i]<=n+3)
		{
			L=numa[i],R=L+m;
			while(L<numa[i+1])sum1=sum2=0,dfs(1),Mdel(ans1,sum1),Mdel(ans2,sum2),++L,++R;
			continue;
		}
		for(int j=1;j<=n+3;++j)
		{
			L=numa[i]-1+j,R=numa[i]-1+j+m,sum1=sum2=0,dfs(1);
			v[j]=Cadd(v[j-1],sum1),v2[j]=Cadd(v2[j-1],sum2);
		}
		int tmp=1,X=numa[i+1]-1;
		for(int j=1;j<=n+3;++j)Mmul(tmp,X-(numa[i]+j-1));
		for(int j=1;j<=n+3;++j)
		{
			int va=fr[j-1];
			if((n+3-j)&1)Mmul(va,Cdel(0,fr[n+3-j]));
			else Mmul(va,fr[n+3-j]);
			Mdel(ans1,Cmul(v[j],tmp,power(Cmul(va,X-(numa[i]+j-1)),MOD-2)));
			Mdel(ans2,Cmul(v2[j],tmp,power(Cmul(va,X-(numa[i]+j-1)),MOD-2)));
		}
	}
	write(ans1,'\n',ans2);
}

相關文章