[LOJ2027] [SHOI2016] 黑暗前的幻想乡

题目链接

LOJ:https://loj.ac/problem/2027

洛谷:https://www.luogu.org/problemnew/show/P4336

Solution

这题很像[ZJOI2016]小星星,注意到如果没有每个边集选一条边的限制的话,直接就是一个果的( m matrix tree)定理。

那么有这个限制我们算出来的生成树个数就会有不合法的情况,即一个边集里选多条边,或者说没有用到(n-1)个边集。

那么我们可以算出(f[s])表示至考虑(s)状态的这些边集,随便选的生成树个数,那么这些方案最多也就选到(s)这些边集。

我们可以参照上题进行容斥,对每个(f)乘个((-1)^{n-1-cnt(s)})的系数加起来就好了。

#include<bits/stdc++.h>
using namespace std;

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}

#define lf double
#define ll long long 

#define pii pair<int,int >
#define vec vector<int >

#define pb push_back
#define mp make_pair
#define fr first
#define sc second

#define FOR(i,l,r) for(int i=l, i##_r=r;i<=i##_r;i++) 

const int maxn = 18;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;

int add(int x,int y) {return x+y>=mod?x+y-mod:x+y;}
int del(int x,int y) {return x-y<0?x-y+mod:x-y;}
int mul(int x,int y) {return 1ll*x*y-1ll*x*y/mod*mod;}

int qpow(int a,int x) {
    int res=1;
    for(;x;x>>=1,a=mul(a,a)) if(x&1) res=mul(res,a);
    return res;
}

int inv(int x) {return qpow(x,mod-2);}

int n,r[maxn][maxn],a[18][400],b[18][400],ans;

void ins(int u,int v) {r[u][u]++,r[v][v]++,r[u][v]--,r[v][u]--;}

int calc() {
    int tmp=1;
    FOR(i,1,n-1) {
        if(!r[i][i])
            FOR(j,i+1,n-1) if(r[j][i]) {
                FOR(k,1,n-1) swap(r[i][k],r[j][k]);tmp=-tmp;break;
            }
        FOR(j,1,i-1) {
            int res=mul(r[i][j],inv(r[j][j]));
            FOR(k,1,n-1) r[i][k]=del(r[i][k],mul(res,r[j][k]));
        }
    }if(tmp==-1) tmp=mod-1;
    FOR(i,1,n-1) tmp=mul(tmp,r[i][i]);
    return tmp;
}

void solve(int s) {
    memset(r,0,sizeof r);
    FOR(i,1,n-1) if(s&(1<<(i-1)))
        FOR(j,1,a[i][0]) ins(a[i][j],b[i][j]);
    ans=((n-1-__builtin_popcount(s))&1?del:add)(ans,calc());
}

int main() {
    read(n);
    FOR(i,1,n-1) {
        read(a[i][0]);
        FOR(j,1,a[i][0]) read(a[i][j]),read(b[i][j]);
    }FOR(s,1,(1<<(n-1))-1) solve(s);
    write(ans);
    return 0;
}
原文地址:https://www.cnblogs.com/hbyer/p/10800100.html