P7736 [NOI2021] 路径交点

P7736 [NOI2021] 路径交点

题意

给一个分层 DAG,起点和终点(第一层和最后一层)数量相同,要求路径没有公共点。问所有路径方案中,\(偶数交点方案数-奇数交点方案数\) 是多少。

思路

对于相邻两层,我们每层各选择 \(n\) 个点。题目说的“交点数”其实就是排列的逆序对数。建邻接矩阵,边权为 \(1\)。对邻接矩阵生成的所有 \(n\times n\) 的子方阵,套 LGV 引理,求行列式。然后相加。

然后你发现你不能枚举选择哪 \(n\) 个点啊,因此这里我们规定相邻两层的节点数都恰好是 \(n\)

对于多层的,使用比内柯西公式解决,题目保证第一层和最后一层都是 \(n\) 个点,使得题目可做。

在第一层和最后一层之间加一层,分别构造两个邻接矩阵 \(A,B\),表示第一层和中间层,最后一层和中间层的连边关系。

思考 \(|AB|\) 的含义。等于所有在 \(A\) 中选择 \(n\) 列(中间层选择 \(n\) 个点),在 \(B\) 中选择对应的 \(n\) 行(中间层选择对应的 \(n\) 个点)。两个方阵的行列式相乘,就是一个卷积形式,就是每种方案的贡献相乘,发现是对的。(过几天我将会看不懂我在说什么)

然后由于矩阵乘法满足结合律,因此所有邻接矩阵连乘,的行列式,就是答案!

时间复杂度 \(O(n^4)\)。常数小吧。

code

警示后人

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace pragmatic {
    constexpr int N=205,mod=998244353;
    int add(int a,int b) { return a+b>=mod ? a+b-mod : a+b; }
    void _add(int &a,int b) { a=add(a,b); }
    int mul(int a,int b) { return 1ll*a*b%mod; }
    void _mul(int &a,int b) { a=mul(a,b); }
    int t,k;
    int n,m[N];
    int u,v;
    struct juzhen {
        int n,m;
        int x[N][N];
        void clear() { memset(x,0,sizeof(x)); }
        juzhen operator * (const juzhen b) const {
            juzhen c={n,b.m};
            c.clear();
            rep(i,1,c.n) rep(j,1,c.m) {
                rep(k,1,m) _add(c.x[i][j],mul(x[i][k],b.x[k][j]));
            }
            return c;
        }
    }a[N];
    int f;
    int ksm(int a,int b=mod-2) {
        int s=1;
        while(b) {
            if(b&1) _mul(s,a);
            _mul(a,a);
            b>>=1;
        }
        return s;
    }
    void _swap(juzhen &a,int x,int y) {
        f^=1;
        rep(i,1,a.n) swap(a.x[x][i],a.x[y][i]);
    }
    void gauss(juzhen &a) {
        rep(i,1,a.n) {
            if(!a.x[i][i]) 
                rep(j,i+1,a.n) {
                    if(a.x[j][i]) {
                        _swap(a,i,j);
                        break;
                    }
                }
            int inv=ksm(a.x[i][i]);
            rep(j,i+1,a.n) {
                if(!a.x[j][i]) continue;
                int t=mul(inv,a.x[j][i]);
                rep(k,i,a.n) _add(a.x[j][k],mod-mul(t,a.x[i][k]));
            }
        }
    }
    int det(juzhen &a) {
        f=0;
        gauss(a);
        int ans=1;
        rep(i,1,a.n) {
            _mul(ans,a.x[i][i]);
        }
        if(f) return add(mod,-ans);
        return ans;
    }
    void main() {
        sf("%d",&t);
        while(t--) {
            sf("%d",&k);
            rep(i,1,k) sf("%d",&n), a[i].n=n;
            rep(i,1,k-1) a[i].m=a[i+1].n;
            rep(i,1,k-1) sf("%d",&m[i]);
            rep(i,1,k-1) a[i].clear();
            rep(i,1,k-1) {
                rep(j,1,m[i]) {
                    sf("%d%d",&u,&v);
                    a[i].x[u][v]=1;
                }
            }
            rep(i,2,k-1) a[1]=a[1]*a[i];
            pf("%d\n",det(a[1]));
        }
    }
}
int main() {
    #ifdef LOCAL
    freopen("in.txt","r",stdin);
    freopen("my.out","w",stdout);
    #endif
    pragmatic :: main();
}
posted @ 2025-01-03 08:24  wing_heart  阅读(28)  评论(0)    收藏  举报