[SHOI2016]黑暗前的幻想乡
作者:互联网
Link
Description
有 \(n-1\) 个边集,从每个边集里选一条边,使得有 \(n\) 个点的图 \(G\) 连通,求方案数。
Solution
如果直接把所有边建好矩阵跑 Matrix-tree 的话,可能会有多条边来自同一边集,同时有些边集没有选到。所以强制不选其中 \(k\) 个边集的边,然后建图,每次都跑 Matrix-tree,再乘上容斥系数 \((-1)^k\)。这样就得到了答案。
复杂度 \(O(2^nn^3\log n)\)。这样算下来有十多亿,但上界很宽,跑不满。实际上一秒就出来了。
#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 20
#define ll long long
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
ll ans=0,a[N][N];
struct E{
int u,v;
inline void readin(){u=read(),v=read();}
inline void add(){a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--;}
inline void del(){a[u][u]--,a[v][v]--,a[u][v]++,a[v][u]++;}
}sta[N][N*N];
int n,top[N];
ll b[N][N];
const ll Mod=1000000007ll;
ll gs(){
int S=n-1;
ll ret=1;
for(int i=1;i<=S;i++)
for(int j=1;j<=S;j++)
b[i][j]=(a[i][j]%Mod+Mod)%Mod;
for(int i=1;i<=S;i++){
for(int j=i+1;j<=S;j++)
while(b[j][i]){
ll t=b[i][i]/b[j][i];
for(int k=i;k<=S;k++)
b[i][k]=(b[i][k]-t*b[j][k]%Mod+Mod)%Mod;
swap(b[i],b[j]),ret=-ret;
}
ret=(ret*b[i][i]%Mod+Mod)%Mod;
if(!ret) return 0;
}
return ret;
}
void dfs(int st,int op){
if(st==n) ans=(ans+op*gs()%Mod+Mod)%Mod;
else{
for(int i=1;i<=top[st];i++)
sta[st][i].add();
dfs(st+1,op);
for(int i=1;i<=top[st];i++)
sta[st][i].del();
dfs(st+1,-op);
}
}
int main(){
n=read();
for(int i=1;i<n;i++){
top[i]=read();
for(int j=1;j<=top[i];j++)
sta[i][j].readin();
}
dfs(1,1);
printf("%lld",ans);
}
标签:幻想,Matrix,黑暗,int,long,边集,while,SHOI2016,getchar 来源: https://www.cnblogs.com/wwlwQWQ/p/14348126.html