C 回家(home.pas/c/cpp)
TL:3S ML:512MB
【Description】
Alice和Bob是一对好兄妹。有一天放学后,他们打算回家。但是,他们还想在路上多玩一会。现在他们有一张地图,地图上标注了小镇的所有的N个地点,以及这些地点之间的道路(道路是无向的),共M条。
在这N个地点中,有K个地点他们希望一定经过。同时,他们不希望在外面玩太久,所以他们要求经过的点总数不能超过R个,为了使得回家的路途不要太无聊,他们要求经过的点不能少于L个。他们想知道共有多少条满足要求的回家的路径。两条路径被认为是不同的,当且仅当至少有一条边不同。
如果用图论语言描述,则为:给定无向图G,求以学校为起点,家为终点,边数在L到R之间,且路径上相邻两个点均不同的路径的数量,结果对109+9取模。
【Input】
第一行五个整数N,M,K,L, R,保证L<=R。
接下来M行,每行两个整数x, y,表示一条无向边,其两个端点的编号为x与y。保证自环
接下来K行,每行一个整数,表示他们要经过的地点的编号。保证这K个点不是家和学校,也不会有重复的点。
这里,家的节点编号为1,学校的节点编号为2,其余所有节点的编号为3..N中的一个整数。
【Output】
一行一个整数,表示路径的条数。
【Sample Input】
57 1 2 3
15
23
54
53
13
32
12
3
【Sample Output】
9
【Hint】
测试点 | N的上界 | K的上界 | 其他 |
1 | N/A | 0 | L=R |
2 | N/A | 0 | N/A |
3 | 10 | 3 | L=R |
4 | 10 | 3 | R-L<5 |
5 | N/A | N/A | L=R |
6 | N/A | N/A | R-L<5 |
7 | 20 | N/A | N/A |
8 | 20 | N/A | N/A |
9 | N/A | N/A | N/A |
10 | N/A | N/A | N/A |
对于100%:N<=30, M<=300, K<=4, 1<=L<=R<=109
首先明确,图的邻接矩阵的x次方即为每个点走x次到另一个点的方案数。所以这题如果没有k直接暴力即可。
有k的话因为k极其小,所以二进制枚举即可,容斥原理,减奇加偶
但是我们注意:只能求出来某个距离时的方案数而得不到前缀,所以我们从终点向超级汇点连一条有向边,超级汇点连一条自环,这样就能前缀累加了,要注意最后答案是ans(r+1)-ans(l)
注意:__builtin_popcount(x)表示x的二进制下1的个数
然后代码理解起来几乎就没有压力,我感觉自己写的比其他julao们写的简洁一些(借鉴hjy julao的思路了)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long
#define ljm 1000000009
int read()
{
int res=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){res=res*10+c-'0';c=getchar();}
return res*f;
}
int n,m,k,l,r,vis[32],num[32];
struct M
{
ll a[32][32];
M(){memset(a,0,sizeof(a));}
}p,t;
M operator * (M a,M b)
{
M c;
for(int i=1;i<=n+1;i++)
for(int j=1;j<=n+1;j++)
for(int k=1;k<=n+1;k++)
c.a[i][j]=(c.a[i][j]+(a.a[i][k]*b.a[k][j])%ljm)%ljm;
return c;
}
M pow(M a,int b)
{
M q;
for(int i=1;i<=n+1;i++) q.a[i][i]=1;
while(b)
{
if(b&1) q=q*a;
a=a*a;
b>>=1;
}
return q;
}
ll cal(int x)
{
ll res=0;
for(int st=0;st<(1<<k);st++)
{
t=p;
for(int i=1;i<=n;i++) vis[i]=1;
for(int i=0;i<k;i++)
if(st&(1<<i))
vis[num[i+1]]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!vis[i]||!vis[j])
t.a[i][j]=0;
t=pow(t,x);
if(__builtin_popcount(st)&1) res=(res-t.a[2][n+1]+ljm)%ljm;
else res=(res+t.a[2][n+1]+ljm)%ljm;
}
return res;
}
int main()
{
freopen("home.in","r",stdin);freopen("home.out","w",stdout);
n=read();m=read();k=read();l=read();r=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
p.a[u][v]++;
p.a[v][u]++;
}
p.a[n+1][n+1]++;
p.a[1][n+1]++;
for(int i=1;i<=k;i++) num[i]=read();
printf("%lld\n",(cal(r+1)-cal(l)+ljm)%ljm);
}