zoukankan      html  css  js  c++  java
  • 【luogu P7736】路径交点(LGV引理)(DP)(矩阵乘法)

    路径交点

    题目链接:luogu P7736

    题目大意

    给你一个分层图,第一层和最后一层的点数相同。
    然后要从第一层的 n 个点走到最后一层的 n 个点,每个点到达的位置互不相同。
    然后问你偶数个交点的路径方案数比奇数个交点的路径方案数多多少个。

    思路

    不难发现一个事情,对于两个起点 (s_i<s_j),如果它们的终点有 (t_i<t_j),那么它相交的次数就是偶数次,否则就是奇数次。

    这个其实不难理解,相交就两个的相对位置交换,那交换两次就相当于没有交换。

    然后你会发现你如果把它压缩成走一步,你会发现就是要不相交,而且就是 LGV 引理的内容!

    那我们就可以 DP 出从一个起点走到一个终点能有的路径数,然后用这个矩阵求行列式。
    (其实这个 DP 可以当做是每两层之间的矩阵依次乘起来得到的矩阵)

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define mo 998244353
    
    using namespace std;
    
    int T, k, n[101];
    int x, y, m[101];
    ll f[2][201][101], a[101][101];
    
    ll work() {//求行列式
    	ll zf = 1, ans = 1, tmp;
    	for (int i = 1; i <= n[1]; i++) {
    		int k = i;
    		for (int j = i + 1; j <= n[1]; j++)
    			if (a[j][i] > a[k][i]) k = j;
    		if (!a[k][i]) return 0;
    		if (k != i) swap(a[i], a[k]), zf = -zf;
    		for (int j = i + 1; j <= n[1]; j++) {
    			if (a[i][i] < a[j][i]) swap(a[i], a[j]), zf = -zf;
    			while (a[j][i]) {
    				tmp = a[i][i] / a[j][i];
    				for (int k = i; k <= n[1]; k++)
    					a[i][k] = (a[i][k] + a[j][k] * (mo - tmp) % mo) % mo;
    				swap(a[i], a[j]); zf = -zf;
    			}
    		}
    		
    		ans = ans * a[i][i] % mo;
    	}
    	
    	if (zf == -1) return (-ans + mo) % mo;
    	return ans;
    }
    
    int main() {
    	scanf("%d", &T);
    	while (T--) {
    		scanf("%d", &k);
    		for (int i = 1; i <= k; i++) scanf("%d", &n[i]);
    		
    		for (int i = 2; i <= k; i++) {
    			scanf("%d", &m[i]);
    		}
    		memset(f[1 & 1], 0, sizeof(f[1 & 1]));
    		for (int i = 1; i <= n[1]; i++) f[1 & 1][i][i] = 1;
    		for (int i = 2; i <= k; i++) {//DP 转移(其实也是矩阵相乘)
    			memset(f[i & 1], 0, sizeof(f[i & 1]));
    			for (int j = 1; j <= m[i]; j++) {
    				scanf("%d %d", &x, &y);
    				for (int k = 1; k <= n[1]; k++) {
    					f[i & 1][y][k] += f[(i - 1) & 1][x][k];
    					f[i & 1][y][k] %= mo;
    				}
    			}
    		}
    		
    		for (int i = 1; i <= n[1]; i++)
    			for (int j = 1; j <= n[k]; j++)
    				a[i][j] = f[k & 1][j][i];
    		
    		printf("%lld
    ", work());
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    (Android)如何将一个高复用性项目供其他项目使用(jar导出,导入,Is Library)(转)
    Android:SlidingMenu 使用详解 .
    单项链表和双向链表的区别
    LinkedList 与 ArrayList的区别
    完全二叉树与满二叉树
    C/C++之回调函数
    C++静态库与动态库
    C++项目中的extern "C" {}
    C++强大背后
    移动优先与响应式Web设计
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P7736.html
Copyright © 2011-2022 走看看