BZOJ1875 [SDOI2009]HH去散步 矩阵

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1875


题意概括

  在一个无向图(有重边无自环)中走,不能在经过连续经过某一条边2次。

  现在走t步,问有多少中从A到B的方案。

  答案mod 45989

  点数<=20,边数<=60,t<=230


题解

  一开始没看到不能来回走这一个条件,所以还以为是一道水题。

  发现这个之后,思考一下,发现还是一道水题。

  如果没有这个限制条件,那么我们按照点构建矩阵,用快速幂优化就可以了。

  但是有了这个之后就稍微难一些。

  我们发现边数很少,所以我们从边开始考虑。

  我们发现可以按照边构建矩阵,表示从某条边到某条边的方案数。

  这样可以避免来回。

  边数=60*2=120,1203*log(230)可以过去的。


代码

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
const int N=20+5,M=60*2+5,mod=45989;
int n,m,t,A,B;
struct Gragh{
	int cnt,x[M],y[M],nxt[M],fst[N];
	void set(){
		cnt=0;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b){
		x[++cnt]=a,y[cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g;
struct Mat{
	int v[M][M];
	void set(int x){
		memset(v,0,sizeof v);
		if (x!=1)
			return;
		for (int i=1;i<=m;i++)
			v[i][i]=1;
	}
	Mat operator * (Mat x){
		Mat ans;
		ans.set(0);
		for (int i=1;i<=m;i++)
			for (int j=1;j<=m;j++)
				for (int k=1;k<=m;k++)
					ans.v[i][j]=(ans.v[i][j]+v[i][k]*x.v[k][j])%mod;
		return ans;
	}
}M0,M1,M2,M3;
Mat MatPow(Mat x,int y){
	Mat ans,now=x;
	ans.set(1);
	while (y){
		if (y&1)
			ans=ans*now;
		now=now*now;
		y>>=1;
	}
	return ans;
}
int op(int x){
	if (x%2==0)
		return x-1;
	return x+1;
}
int main(){
	scanf("%d%d%d%d%d",&n,&m,&t,&A,&B);
	g.set();
	for (int i=1,a,b;i<=m;i++){
		scanf("%d%d",&a,&b);
		g.add(a,b),g.add(b,a);
	}
	m<<=1;
	M0.set(0);
	for (int i=1;i<=m;i++){
		for (int j=g.fst[g.y[i]];j;j=g.nxt[j])
			if (j!=op(i))
				M0.v[i][j]++;
	}
	M1.set(0);
	for (int i=g.fst[A];i;i=g.nxt[i])
		M1.v[1][i]++;
	M2=MatPow(M0,t-1);
	M3=M1*M2;
	int ans=0;
	for (int i=1;i<=m;i++)
		if (g.y[i]==B)
			ans=(ans+M3.v[1][i])%mod;
	printf("%d",ans);
	return 0;
}

  

 

posted @ 2017-09-16 18:45  zzd233  阅读(265)  评论(0编辑  收藏  举报