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;
證明略。
STREAMING #5 題解 3.高位網絡