「ZJOI2016」小星星 题解
已有的题解都为较为基础的容斥算法,这种容斥的正确性实际上并不显然。
在此,给出一种基于
设计
对于子集卷积,我们可以使用占位多项式在复杂度多乘一个
但实际上并不需要占位多项式,考虑直接进行异或卷积的过程。
容易发现我们最后求的是
于是我们证明了直接异或卷积的正确性。
接下来将转移写出,便有一个
这种做法似乎已经没有优化空间了,让我们看一下代码:
IV dfs(ll u,ll F){
for(ll v:g[u])if(v^F)dfs(v,u);
F(pu,1,n){
f[u][pu][1<<pu-1]=1;FWT(f[u][pu]);//third line
for(ll v:g[u])if(v^F){
mem(tmp,0);
F(pv,1,n)if(vis[pu][pv])F(j,0,S)tmp[j]+=f[v][pv][j];
FWT(tmp);F(i,0,S)f[u][pu][i]=f[u][pu][i]*tmp[i];//first line
}
IFWT(f[u][pu]);//second line
}
}
我们发现 first line 标记处的一次 second line 处进行了一次
于是我们想到记录每个点的
但我们和还有一个问题没有解决,在 third line 处的一行
我们回归
我们在一系列的优化后,尽管复杂度不变,但具有更加优美的形式,且不需要用到
因此常数有显著的下降,笔者实现后发现可以通过此题。
const int maxn = 20;
const int maxS = (1<<17);
ll n,S,f[maxn][maxn][maxS];bool vis[maxn][maxn];
vector<ll>g[maxn];IV add(ll u,ll v){g[u].push_back(v);}
ll tmp[maxS];IV print(ll S){F(i,0,n-1)putchar((S>>i&1)+'0');}ll tot;
IV dfs(ll u,ll F){
for(ll v:g[u])if(v^F)dfs(v,u);
F(pu,1,n){
F(k,0,S)f[u][pu][k]=(__builtin_popcount((1<<pu-1)&k)&1)?-1:1;
for(ll v:g[u])if(v^F){
mem(tmp,0);
F(pv,1,n)if(vis[pu][pv])F(j,0,S)tmp[j]+=f[v][pv][j];
F(i,0,S)f[u][pu][i]=f[u][pu][i]*tmp[i];
}
}
}
int main(){
n=read();S=(1<<n)-1;ll m=read();
while(m--){ll x=read(),y=read();vis[x][y]=vis[y][x]=1;}
F(i,1,n-1){ll x=read(),y=read();add(x,y);add(y,x);}dfs(1,0);ll ans=0;
F(i,1,n){ll tmp=0;F(j,0,S)tmp+=f[1][i][j]*((__builtin_popcount(S&j)&1)?-1:1);ans+=tmp/(S+1);}
return cout<<ans,0;
}
闲话:这里的 FWT 可能实际上严谨的证明了容斥的正确性?