hdu 5852 :Intersection is not allowed! 行列式

有K个棋子在一个大小为N×N的棋盘。一开始,它们都在棋盘的顶端,它们起始的位置是 (1,a1),(1,a2),...,(1,ak) ,它们的目的地是 (n,b1),(n,b2),...,(n,bk)。

一个位于 (r,c) 的棋子每一步只能向右走到 (r,c+1) 或者向下走到 (r+1,c) 。

我们把 i 棋子从 (1,ai) 走到 (n,bi) 的路径记作 pi 。

你的任务是计算有多少种方案把n个棋子送到目的地,并且对于任意两个不同的棋子 i,j ,使得路径 pi 与 pj 不相交(即没有公共点)。

 

 

容斥原理。

假设只有两个点,那么答案就是它们以任意路径到达终点的方案数减去相交的方案。

比如 a1->b1 ,a2->b2 ,那它们相交的方案就是 a1->b2,a2->b1的所有方案。

因为在最后一个交点下把两条路径换一下它们是一一对应的。

扩展到多个点时有$n!$种向下对应对应的方案,每个方案的容斥系数是$-1^{逆序对个数}$。

实际上这东西就是矩阵的行列式。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define ll long long
 6 #define N 105
 7 using namespace std;
 8 int n,k;
 9 const int inf = 200000;
10 const int p = 1000000007;
11 int jie[200005],ni[200005];
12 void yu()
13 {
14     jie[0]=ni[0]=ni[1]=1;
15     for(int i=1;i<=inf;i++)jie[i]=1LL*i*jie[i-1]%p;
16     for(int i=2;i<=inf;i++)ni[i]=(1LL*(-p/i)*ni[p%i]%p+p)%p;
17     for(int i=1;i<=inf;i++)ni[i]=1LL*ni[i-1]*ni[i]%p;
18 }
19 int pw(ll x,int y)
20 {
21     ll lst=1;
22     while(y)
23     {
24         if(y&1)lst=lst*x%p;
25         y>>=1;
26         x=1LL*x*x%p;
27     }
28     return (int)lst;
29 }
30 int c(int n,int m)
31 {
32     return 1LL*jie[n]*ni[m]%p*ni[n-m]%p;
33 }
34 int st[N],ed[N];
35 ll a[N][N];
36 void guess()
37 {
38     ll ans=1;
39     for(int i=1;i<=k;i++)
40     {
41         for(int j=i;j<=k;j++)
42         {
43             if(a[j][i])
44             {
45                 if(j!=i)ans*=-1;
46                 for(int l=1;l<=k;l++)swap(a[i][l],a[j][l]);
47                 break;
48             }
49         }
50         ll tp=pw(a[i][i],p-2);
51         for(int j=i+1;j<=k;j++)
52         {
53             if(a[j][i])
54             {
55                 ll tmp=p-tp*a[j][i]%p;
56                 for(int l=i;l<=k;l++)
57                 {
58                     a[j][l]=(a[j][l]+tmp*a[i][l]%p)%p;
59                 }
60             }
61         }
62     }
63     if(ans==-1)ans=p-1;
64     for(int i=1;i<=k;i++)ans=ans*a[i][i]%p;
65     printf("%lld\n",ans);
66     return ;
67 }
68 int main()
69 {
70     yu();
71     scanf("%d%d",&n,&k);
72     for(int i=1;i<=k;i++)scanf("%d",&st[i]);
73     for(int i=1;i<=k;i++)scanf("%d",&ed[i]);
74     for(int i=1;i<=k;i++)
75     {
76         for(int j=1;j<=k;j++)
77         {
78             if(ed[j]>=st[i])a[i][j]=c(n-1+abs(ed[j]-st[i]),n-1);
79         }
80     }
81     guess();
82     return 0;
83 }

 

转载于:https://www.cnblogs.com/ezyzy/p/6670187.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值