STREAMING #5 题解 3.高位网络
高维网络
【题目描述】
现在有一个 d 维的坐标网格,其中第 i 维坐标的范围是[0,a_i]。在这个范围内建立一个有向图:我们把范围内的每个整点(每一维坐标均为整数的点)当做图上的顶点。设点 A(0,0,⋯,0),B(a_1,a_2,⋯,a_d)。对于范围内的点(x_1,x_2,⋯,x_d),它会向以下这些点(如果目标点在范围内)连有向边:(x_1+1,x_2,⋯,x_d),(x_1,x_2+1,⋯,x_d),⋯,(x_1,x_2,⋯,x_d+1)
现在从点 A 到点 B 会有若干条路径,路径的条数可以十分简单地算出。然而不幸的是,范围内有 p 个点被破坏了(点 A 和点 B 不会被破坏) ,其中第 i个点的坐标为(x_(i,1),x_(i,2),⋯,x_(i,d))。你需要算出从点 A 到点B 剩余的路径条数。
由于答案可能很大,你只需要输出它对 1,000,000,007 取模的结果。
【输入格式】
第一行为两个整数 d,p。
第二行为 d 个整数,其中第 i 个数是 a_i。
接下来 p 行,每行 d 个整数,其中第 i 行第 j 个数是 x_(i,j)。
【输出格式】
一个整数,表示从点 A 到点 B 剩余的路径条数对 1,000,000,007 取模的结果。
【输入样例】
2 1
2 1
1 0
【输出样例】
1
【数据范围】
30分算法
• 在前30分当中,数据规模非常小,可以暴搜每条路线。
d=1的算法
• 对于d=1的情况,如果没有点被破坏则答案是1,否则答案是0。
p=0的算法
•运用排列组合公式:二维:C(n,n+m);
多维:
阶乘可以暴力算,注意除法要用逆元(费马小定理求逆元)算。
【题解】【dp+组合数+容斥原理】
【f[i]表示从A到i不经过被破坏的点的路径条数;g[i][j]表示从i到j可以经过的被破坏的点的方案数(可以用组合数求)】
【g[i][j]:(各维度权值之和的阶乘)/(各维度阶乘之积),如二维情况:(行数+列数)!/(行数!×列数!)】
【f[i]=总路径条数-不合法,f[x]=g[A][x]-Σf[y]*g[y][x]】
【最后输出f[B]】
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<algorithm> 5 #define man 10000010 6 #define sc(x) scanf("%d",&x) 7 #define ll long long 8 #define mod 1000000007 9 using namespace std; 10 struct node 11 { 12 int x[110]; 13 }a[510];//每个点的坐标 14 ll mi[man],sum[510],f[510],g[510][510],h[510]; 15 /* 16 mi[]:求i的阶乘; 17 sum[]:每个点的坐标和(为了之后的p=0做准备); 18 f[]:从A到i不经过被破坏的点的路径条数; 19 g[][]:从i到j可以经过的被破坏的点的方案数(可以用组合数求); 20 h[]:每两个点坐标之间的差值; 21 */ 22 int d,p; 23 int cmp(node a,node b) 24 { 25 for(int i=1;i<=d;i++) 26 { 27 if(a.x[i]<b.x[i])return 1; 28 if(a.x[i]>b.x[i])return 0; 29 } 30 } 31 // cmp:从一维开始从小到大排序; 32 inline void pow1() 33 { mi[0]=1; 34 for(ll i=1;i<=10000000;i++) 35 mi[i]=(mi[i-1]*i)%mod; 36 } 37 //pow1:计算阶乘; 38 ll bpow(int a,int b) 39 { 40 ll ans=1,base=a; 41 while(b) 42 { 43 if(b&1) ans=ans*base%mod; 44 base=base*base%mod; 45 b>>=1; 46 } 47 return ans; 48 } 49 //快速幂; 50 inline void solve() 51 { 52 for(int i=1;i<=p;i++) 53 { 54 ll t=mi[sum[i]]; 55 ll t1=1; 56 for(int j=1;j<=d;j++) 57 if(a[i].x[j]) 58 t1=(t1*mi[a[i].x[j]])%mod; 59 ll ans=bpow(t1,mod-2); 60 g[0][i]=t*ans%mod; 61 }//运用费马小定理计算从A点至各点的路径总数; 62 for(int i=1;i<=p;i++) 63 for(int j=1;j<=p;j++) 64 { 65 ll tot=0;bool b=1; 66 for(int k=1;k<=d;k++) 67 { 68 h[k]=a[j].x[k]-a[i].x[k]; 69 tot+=h[k]; 70 if(h[k]<0) //因为是单向边(从i至j),所以j-i的坐标差>=0; 71 { 72 b=0; 73 break; 74 } 75 } 76 if(!b) continue; 77 ll t=mi[tot]; 78 ll t1=1; 79 for(int k=1;k<=d;k++) 80 if(h[k]) t1=(t1*mi[h[k]])%mod; 81 ll ans=bpow(t1,mod-2); 82 g[i][j]=t*ans%mod; 83 }//运用费马小定理求两点之间路径总数; 84 return ; 85 } 86 87 int main() 88 { freopen("cube.in","r",stdin); 89 freopen("cube.out","w",stdout); 90 sc(d);sc(p); 91 p++; 92 for(int i=1;i<=d;i++) 93 sc(a[p].x[i]);//每维极值相当于B点(终点)坐标; 94 for(int i=1;i<=p-1;i++) 95 for(int j=1;j<=d;j++) 96 sc(a[i].x[j]); 97 sort(a+1,a+1+p,cmp); 98 for(int i=1;i<=p;i++) 99 for(int j=1;j<=d;j++) 100 {sum[i]+=a[i].x[j];sum[i]%=mod;} 101 pow1(); 102 solve();//预处理g[i][j]; 103 for(int i=1;i<=p;i++) 104 { 105 f[i]=g[0][i]%mod; 106 for(int j=1;j<i;j++) 107 f[i]=(f[i]-f[j]*g[j][i])%mod; 108 f[i]=(f[i]%mod+mod)%mod; 109 } 110 cout<<f[p]<<endl; 111 return 0; 112 }
费马小定理:a/b mod p ==a* b^p-2 mod p;
证明略。