黑暗前的幻想乡[SHOI2016][容斥原理][矩阵树定理]

    技术2022-07-17  75

    文章目录

    题目思路另解代码

    题目

    Luogu n n n 个点,修成一棵树, n − 1 n-1 n1 个公司,每个公司能修一些边,但只能修一条,求方案数

    思路

    由于是树,所以条件转化(最重要的一步): [每个公司修一条边]=[每个公司修边]=[ a 1 a_1 a1 ∩ \cap a 2 a_2 a2 ∩ \cap ∩ \cap a n a_n an 修] 于是 2 n 2^n 2n 枚举公司然后矩阵树即可 时间复杂度 O ( 2 n ∗ n 3 ) O(2^n*n^3) O(2nn3)

    另解

    自己瞎想的 矩阵树每个点看成一个 2 n − 1 2^{n-1} 2n1 项的多项式,每一项为 k ∗ a t 1 a t 2 . . . a t m k*a_{t_1}a_{t_2}...a_{t_m} kat1at2...atm 开始时形如 − a t 1 − a t 2 − . . . -a_{t_1}-a_{t_2}-... at1at2... 或者 a t 1 + a t 2 . . . a_{t_1}+a_{t_2}... at1+at2... 之类的 好像不能消元…咕了 O ( 2 n ∗ n 3 ) O(2^n*n^3) O(2nn3)

    代码

    //#pragma GCC optimize(2) #include<set> #include<map> #include<stack> #include<ctime> #include<cstdio> #include<queue> #include<cmath> #include<vector> #include<cstring> #include<climits> #include<iostream> #include<algorithm> using namespace std; #define LL long long #define ULL unsigned long long int read(){ int f=1,x=0;char c=getchar(); while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();} while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();} return f*x; } #define MAXN 300 #define INF 0x3f3f3f3f #define Mod (int)(1e9+7) LL mat[MAXN+5][MAXN+5],a[MAXN+5][MAXN+5]; LL Pow(LL x,int y){ LL ret=1; while(y){ if(y&1) ret=ret*x%Mod; x=x*x%Mod,y>>=1; } return ret; } int Det(int n){ n--; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) mat[i][j]=(a[i][j]+Mod)%Mod; for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++) if(!mat[i][i]&&mat[j][i]){ swap(mat[i],mat[j]); break; } LL Inv=Pow(mat[i][i],Mod-2); for(int j=i+1;j<=n;j++){ LL tmp=mat[j][i]*Inv%Mod; for(int k=i;k<=n;k++) mat[j][k]=(mat[j][k]-tmp*mat[i][k]%Mod+Mod)%Mod; } } LL ret=1; for(int i=1;i<=n;i++) ret=ret*mat[i][i]%Mod; return ret; } int n; LL ans; vector<pair<int,int> > Edge[MAXN+5]; void DFS(int d,int k){ if(d==n){ ans=(ans+Mod+k*Det(n))%Mod; return ; } DFS(d+1,-k); for(int i=0;i<(int)Edge[d].size();i++){ int u=Edge[d][i].first,v=Edge[d][i].second; a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--; } DFS(d+1,k); for(int i=0;i<(int)Edge[d].size();i++){ int u=Edge[d][i].first,v=Edge[d][i].second; a[u][u]--,a[v][v]--,a[u][v]++,a[v][u]++; } return ; } int main(){ n=read(); for(int i=1;i<n;i++){ int m=read(); for(int j=1;j<=m;j++) Edge[i].push_back(make_pair(read(),read())); } DFS(1,1); printf("%lld\n",ans); return 0; }
    Processed: 0.012, SQL: 10