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 
【输出样例】 

【数据范围】 
这里写图片描述

 

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;

证明略。

 

posted @ 2017-08-29 17:26  Slager_Z  阅读(243)  评论(0编辑  收藏  举报
博客园 首页 私信博主 显示目录 隐藏目录 管理 动画