SHOI2016 黑暗前的幻想乡
题目链接:戳我
幻想乡是个什么东西??(逃
矩阵树定理+容斥
就是设\(dp[i]\)表示至多i个公司修建道路,那么我们有\(ans=dp[n-1]-dp[n-2]+dp[n-3]......\)balabala(就是那个容斥公式嘛qwqwq)
然后每次都跑一次矩阵树定理qwqwq
但是这样的时间复杂度是\(O(n^32^{n-1})\)???所以说。。。跑的。。。2s。。。很勉强qwqwq
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define MAXN 100010
#define mod 1000000007
using namespace std;
int n,maxx,ans;
int cnt[MAXN],dp[20][20];
struct Node{int x,y;};
vector<Node>node[20];
inline int getnum(int x)
{
int cur_ans=0;
for(int i=0;i<=21;i++)
if(x&(1<<i))
cur_ans++;
return cur_ans;
}
inline int matrix_tree()
{
int cur_ans=1;
for(int i=2;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
while(dp[j][i])
{
int t=dp[i][i]/dp[j][i];
for(int k=i;k<=n;k++)
dp[i][k]=(dp[i][k]-1ll*dp[j][k]*t%mod+mod)%mod;
swap(dp[i],dp[j]);
cur_ans=(-cur_ans+mod)%mod;
}
}
cur_ans=(1ll*cur_ans*dp[i][i])%mod;
}
return cur_ans;
}
inline int calc(int x)
{
memset(dp,0,sizeof(dp));
for(int i=1;i<n;i++)
{
if(x&(1<<(i-1)))
{
for(int j=0;j<node[i].size();j++)
{
int u=node[i][j].x,v=node[i][j].y;
dp[u][u]=(dp[u][u]+1)%mod;
dp[v][v]=(dp[v][v]+1)%mod;
dp[u][v]=(dp[u][v]-1+mod)%mod;
dp[v][u]=(dp[v][u]-1+mod)%mod;
}
}
}
return matrix_tree();
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
freopen("ce.out","w",stdout);
#endif
scanf("%d",&n);
maxx=(1<<n-1)-1;
for(int i=1;i<n;i++)
{
int k,u,v;
scanf("%d",&k);
for(int j=1;j<=k;j++)
{
scanf("%d%d",&u,&v);
node[i].push_back((Node){u,v});
}
}
for(int i=0;i<=maxx;i++) cnt[i]=getnum(i);
for(int i=0;i<1<<(n-1);++i)
ans=(ans+(((n-1-cnt[i])&1)?mod-calc(i):calc(i)))%mod;
printf("%d\n",ans);
return 0;
}