zoukankan      html  css  js  c++  java
  • 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;

    证明略。

    欢迎大家吐槽或评论,如要转载请注明地址,谢谢~(虽然不一定有人能看到。。。)
  • 相关阅读:
    606. Construct String from Binary Tree
    696. Count Binary Substrings
    POJ 3255 Roadblocks (次短路)
    POJ 2823 Sliding Window (单调队列)
    POJ 1704 Georgia and Bob (博弈)
    UVa 1663 Purifying Machine (二分匹配)
    UVa 10801 Lift Hopping (Dijkstra)
    POJ 3281 Dining (网络流之最大流)
    UVa 11100 The Trip, 2007 (题意+贪心)
    UVaLive 4254 Processor (二分+优先队列)
  • 原文地址:https://www.cnblogs.com/Slager-Z/p/7449371.html
Copyright © 2011-2022 走看看