【NOIp模拟赛】高维网络

【问题描述】

现在有一个?维的坐标网格,其中第?维坐标的范围是[0, ??]。

在这个范围内建立一个有向图:我们把范围内的每个整点(每一维坐标均为整数的点)当做图上的顶点。设点?(0,0, ⋯ ,0), ?(?1, ?2, ⋯ , ??)。

对于范围内的点(?1, ?2, ⋯ , ??),它会向以下这些点(如果目标点在范围内):

(?1 + 1, ?2, ⋯ , ??), (?1, ?2 + 1, ⋯ , ??), ⋯ , (?1, ?2, ⋯ , ?? + 1)连有向边。

现在从点?到点?会有若干条路径,路径的条数可以十分简单地算出。然而不幸的是,范围内有?个点被破坏了(点?和点?不会被破坏),其中第?个点的坐标为(??,1, ??,2, ⋯ , ??,?)。你需要算出从点?到点?剩余的路径条数。

由于答案可能很大,你只需要输出它对1,000,000,007取模的结果。

 【输入文件】

第一行为两个整数? ?。

第二行为?个整数,其中第?个数是??。

接下来?行,每行?个整数,其中第?行第?个数是??,?。

【输出文件】 

一个整数,表示从点?到点?剩余的路径条数对1,000,000,007取模的结果。

【输入样例】 

2 1

2 1

1 0

 【输出样例】

1

 【样例解释】

如图所示,当删掉点(1,0)后,从点?到点?的 3 条路径只剩下了 1 条。

【数据规模和约定】

 

分析

题解原话(略有改动)

30分,暴力搜索每条路线

对于d=1的情况,如果没有点被破坏则答案是1,否则答案是0;

60分,动态规划

对于p=0的情况

如果看不懂上面的公式的话,可以举一个二维的例子

如果是二维的话,就相当于有一个n*m的方格,每次只能向右或向上走,有的点不能走,求从(0,0,)点到(n,m)点的路径数。这个显然要走n+m步,肯定有n步是向右的,那就是说在n+m步中选n步向右走所以就是C(n,n+m)

就是

当然C(m,n+m)和C(n,n+m)是一样的。意义的话类推一下就行了。

上述算法结合起来是可以得到80分的。

80分算法2

100分

还有要注意的一点是要防止中间过程运算的溢出。

代码 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1000000007;
const int maxn=100+5;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int d,p;
int a[maxn],fac[10000005],sum[maxn*5];
int g[maxn*5][maxn*5],c[maxn],f[maxn*5];//g[i][j]表示i到j的路径数
struct node
{
    int x[maxn];
}b[maxn*5];//那些不能通过的点要用结构体保存好排序。当然你如果要是用递归写的可能不用要。
bool cmp(node a,node b)
{
    for(int i=1;i<=d;i++)
    {
        if(a.x[i]<b.x[i]) return 1;
        if(a.x[i]>b.x[i]) return 0;
    }
}    
inline int power(int a,int n)
{
    int res=1,base=a;
    while(n)
    {
        if(n&1) res=(ll)res*(ll)base%(ll)mod;
        base=(ll)base*(ll)base%(ll)mod;
        n=n>>1;
    }
    return res;
}
int inv(int x)
{return power(x,mod-2);}
void fct()
{
    fac[0]=1;
    for(int i=1;i<=10000000;i++)
        fac[i]=(ll)fac[i-1]*(ll)i%(ll)mod;
}
void prework()
{
    for(int i=1;i<=p;i++)
    {
        int t1,t2=1;
        t1=fac[sum[i]];
        for(int j=1;j<=d;j++)
        if(b[i].x[j])    t2=(ll)t2*(ll)fac[b[i].x[j]]%(ll)mod;
        g[0][i]=(ll)t1*(ll)inv(t2)%(ll)mod;
    }
    for(int i=1;i<=p;i++)
    for(int j=1;j<=p;j++) if(i!=j)
    {
        bool flag=1; int s=0;
        for(int k=1;k<=d;k++)
        {  
            c[k]=b[j].x[k]-b[i].x[k]; 
            s+=c[k];
            if(c[k]<0){  flag=0; break; }
        }
        if(!flag) continue;
        int t1,t2=1;
        t1=fac[s];
        for(int k=1;k<=d;k++)
        if(c[k])    t2=(ll)t2*(ll)fac[c[k]]%(ll)mod;
        g[i][j]=(ll)t1*(ll)inv(t2)%(ll)mod;
    }
}
int main()
{
    freopen("cube.in","r",stdin);
    freopen("cube.out","w",stdout);
    d=read(); p=read();
    for(int i=1;i<=d;i++) a[i]=read();
    for(int i=1;i<=p;i++)
    for(int j=1;j<=d;j++)
    b[i].x[j]=read();
    p++; for(int i=1;i<=d;i++) b[p].x[i]=a[i];
    sort(b+1,b+p+1,cmp);
    for(int i=1;i<=p;i++)
    for(int j=1;j<=d;j++)
    sum[i]+=b[i].x[j];
    fct(); prework();
    for(int i=1;i<=p;i++)
    {
        f[i]=g[0][i];
        for(int j=1;j<i;j++)
        f[i]=((ll)f[i]-((ll)f[j]*(ll)g[j][i])%(ll)mod+mod)%mod;
    }
    printf("%d\n",f[p]);
    fclose(stdin); fclose(stdout);
    return 0;
}
    
    
    

 

转载于:https://www.cnblogs.com/huihao/p/7449839.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值