高维网络(容斥定理+dp)

高维网络

【题目描述】
现在有一个 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
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

program df;
var i,j,n,m,x,y,z,k,d,p:longint;
ans,t,fmmax,fzmax:int64;
fz:array[0..10000000] of int64;
fm,sum,f:array[0..100000] of int64;
map:array[0..600,0..600] of longint;

procedure check(a,b:int64;var x,y:int64);
var t:int64;
begin
if b=0 then
begin
x:=1;
y:=0;
exit;
end;
check(b,a mod b,x,y);
t:=x;
x:=y;
y:=t-(a div b)*y;
end;

function deal(a:int64):int64;
var x,y:int64;
begin
check(a,k,x,y);
exit(x);
end;

function search(a,b:int64):int64; //a到b点的方案总数
var i,j:longint;
ans:int64;
begin
for i:=1 to d do
if map[a,i]>map[b,i] then exit(-1);
ans:=fz[sum[b]-sum[a]];
for i:=1 to d do
ans:=(ans*fm[map[b,i]-map[a,i]]) mod k;
exit((ans+k) mod k);
end;

function dp(x:int64):int64; //求取0到x的方案总数
var i,j:longint;
ans,kk:int64;
begin
if f[x]<>0 then exit(f[x]);
ans:=search(0,x);
for i:=1 to p do
if i<>x then
begin
kk:=search(i,x);
if kk=-1 then continue;
ans:=(ans-(dp(i)*kk) mod k) mod k; //用容斥定理算出0到x的方案总数
end;
f[x]:=ans;
exit((ans+k) mod k);
end;

begin
assign(input,’cube.in’);
reset(input);
assign(output,’cube.out’);
rewrite(output);
k:=1000000007;
readln(d,p);
p:=p+1; //把最终点作为一个点算入
for i:=1 to d do
begin
read(map[p,i]);
sum[p]:=sum[p]+map[p,i];
if map[p,i]>fmmax then fmmax:=map[p,i]; //算分母的最大值
end;
fzmax:=sum[p];
for i:=1 to p-1 do
begin
for j:=1 to d do
begin
read(map[i,j]);
sum[i]:=sum[i]+map[i,j];
if map[i,j]>fmmax then fmmax:=map[p,i];
end;
if sum[i]>fzmax then fzmax:=sum[i]; //计算分子的最大值
readln;
end;
fz[0]:=1;
for i:=1 to fzmax do
fz[i]:=(fz[i-1]*i) mod k; //求取分子,即阶乘
fm[0]:=1;
for i:=1 to fmmax do
begin
fm[i]:=(fm[i-1]*deal(i)) mod k; //算出分母的乘法逆元
end;
writeln(dp(p));
close(input);
close(output);
end.

posted @ 2017-08-29 16:58  Gxyhqzt  阅读(127)  评论(0编辑  收藏  举报