1. 程式人生 > 其它 >「ZJOI2016」小星星

「ZJOI2016」小星星

「ZJOI2016」小星星

題目連結:https://loj.ac/p/2091


前置知識:\(FWT\)

(建議看這個部落格:https://www.cnblogs.com/chasedeath/p/12785842.html)

一眼狀壓,考慮寫出最暴力的 dp。

\(f(u,a,sta)\) 表示以 \(u\) 為根的子樹且 \(u\) 的編號為 \(a\) 且用掉的編號集合為 \(sta\)

那麼對於每個點列舉自己的兒子 \(v\), 並且列舉 \(a,b\) 分別表示 \(u,v\) 的編號,再列舉子集進行統計即可。

\(\oplus\) 表示異或,且下文所有寫到的 \(b\) 僅指滿足原飾品中與

\(a\) 相連的編號。即下文提到的 \(b\) 皆滿足題給的合法條件。

那麼就有:

\[f(u,a,S)=\sum_{v}\sum_{b}\sum_{T\subseteq S}f(v,b,T)\times f(u,a,T\oplus S) \]

考慮使用字首和優化,處理出對於每個 \(a\) 合法的 \(f(v,b,T)\) 之和。

那麼設 \(g(v,T)_a=\sum_bf(v,b,T)\)

就有:

\[f(u,a,S)=\sum_{v}\sum_{T\subseteq S} g(v,T)_a\times f(u,a,T\oplus S) \]

直接根據這個式子計算答案時間複雜度是 \(O(n^2\times3^n)\)

的。

而注意到這個式子的形式跟下面這個式子的形式非常相似:

\[f_S=\sum_{R\oplus T=S} g_R\times h_T \]

那麼我們將原式變換一下:

\[f(u,a,S)=\sum_v\sum_{T\oplus R=S,T\cap R=\varnothing} g(v,T)_a\times f(u,a,R) \]

由於 \(T\cap R=\varnothing\) 這個條件比較苛刻,直接 \(FWT\) 的話單次需要 \(O(n^2\times2^n)\)。總和就要 \(O(n^4\times2^n)\)了。

考慮去掉這個條件。

\(pc(i)\) 表示將 \(i\)

用二進位制表示後有幾個 \(1\)

對於所有值不為 \(0\)\(f(u,a,R_1),f(u,a,R_2)\dots f(u,a,R_k)\),一定有 \(pc(R_1)=pc(R_2)=\dots pc(R_k)\)

因為每個點恰有一個編號,所以對於 \(pc(R)\not = siz_u\)\(siz_u\) 表示 \(u\) 的子樹大小),\(f(u,a,R)=0\)

(同時 \(siz_u\) 也可以表示加入兒子 \(v\) 之前加入的總點數)。

再考慮一下異或的性質:若有 \(pc(S)+pc(T)=pc(S\oplus T)\) ,則有 \(S\cap T=\varnothing\),否則 \(S\cap R\not=\varnothing\)

所以原式子可以直接變成:

\[f(u,a,S)=\sum_v\sum_{T\oplus R=S} g(v,T)_a\times f(u,a,R) \]

之後對於所有 \(S\) 滿足 \(pc(S)\not= siz_u+siz_v\),我們手動賦值使 \(f(u,a,S)=0\) 即可。

那麼對於後面這個式子:

\[\sum_{T\oplus R=S} g(v,T)_a\times f(u,a,R) \]

直接用 \(FWT\) 處理即可,單次複雜度是 \(O(n\times 2^n)\)。總時間複雜度就是 \(O(n^3\times 2^n)\)

此外,由於空間限制不能直接開空間為 \(O(n^2\times 2^n)\) 的陣列。但是考慮除了 \(pc(S)=siz_u\) 的情況下其餘 \(f\) 值皆為 \(0\)。而滿足 \(pc(S)=siz_u\)\(S\) 最多隻有 \(2.5e4\) 個,所以離散化一下即可。

但是常數極大,可以通過 LOJ 但是不能通過 luogu。

程式碼如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 18;
bool Sunny;
ll f[MAXN][MAXN][30005],res[(1<<17)],C[(1<<17)],B[(1<<17)];
int A[MAXN][30005],S[MAXN],siz[MAXN];
int id[(1<<17)+5];
int n,m;
bool link[MAXN][MAXN],e[MAXN][MAXN];
bool Small;
inline void FWT()
{
	for(int l=1;l<(1<<n);l<<=1)
		for(int i=0;i<(1<<n);i+=2*l)
			for(int j=i;j<i+l;++j)
			{
				ll t=B[j+l];
				B[j+l]=B[j]-t;B[j]+=t;
				t=C[j+l];
				C[j+l]=C[j]-t;C[j]+=t;
			}
}
inline void IFWT()
{
	for(int l=1;l<(1<<n);l<<=1)
		for(int i=0;i<(1<<n);i+=2*l)
			for(int j=i;j<i+l;++j)
			{
				ll t=res[j+l];
				res[j+l]=res[j]-t;res[j]+=t;
			}
	for(int i=0;i<(1<<n);++i) res[i]/=(1<<n);
}
inline void get()
{
	for(int i=0;i<(1<<n);++i) res[i]=B[i]*C[i];
}
void dfs(int p,int fa)
{
	for(int i=1;i<=n;++i)
		f[p][i][id[1<<(i-1)]]=1;
	int Siz=1;
	for(int v=1;v<=n;++v)
	{
		if(!e[p][v]||v==fa) continue;
		dfs(v,p);Siz+=siz[v];
		for(int a=1;a<=n;++a)
		{
			memset(C,0,sizeof C);memset(B,0,sizeof B);
			for(int i=1;i<=S[Siz-siz[v]];++i) C[A[Siz-siz[v]][i]]=f[p][a][i];
			for(int b=1;b<=n;++b)
			{
				if(link[a][b])
					for(int i=1;i<=S[siz[v]];++i)
						B[A[siz[v]][i]]+=f[v][b][i];
			}
			FWT();get();IFWT();
			for(int i=1;i<=S[Siz];++i) f[p][a][i]=res[A[Siz][i]];
		}
	}
	siz[p]=Siz;
}
int main()
{
//	cout<<1.0*(&Small-&Sunny)/1024/1024<<"MB"<<endl;
	scanf("%d %d",&n,&m);
	for(int i=1;i<(1<<n);++i)
	{
		int cnt=__builtin_popcount(i);
		A[cnt][++S[cnt]]=i;
		id[i]=S[cnt];
	}
	for(int i=1;i<=m;++i)
	{
		int u,v;
		scanf("%d %d",&u,&v);
		link[u][v]=link[v][u]=1;
	}
	for(int i=1;i<n;++i)
	{
		int u,v;
		scanf("%d %d",&u,&v);
		e[u][v]=e[v][u]=1;
	}
	dfs(1,0);
	ll ans=0;
	for(int i=1;i<=n;++i) ans+=f[1][i][id[(1<<n)-1]];
	printf("%lld\n",ans);
	return 0;
}