【Bzoj1875】HH去散步

【Bzoj1875】HH去散步

先说一下边点互化的思路(貌似这种题不多?),以后看见边数少的要死的记得想边点乎化,将无向边变成有向边在考虑边之间的可达性,如果边x的终点是边y的起点(前提不是同一条边),则连一条x到y的边,表示从x可以走到y,同一条无向边转化成的两条有向边不联通(可以用最大流中异或1的方法记录两条有向边,当然也可以用一个数组记录,不过代码看起来稍恶心一点),这样就解决了题目中的限制。然后考虑如何求从a->b的长度为t的方案数,将边形成的矩阵乘t-1次,相当于经过了k-1个点,加上起点和终点k+1,中间正好经过k条边,然后有两个思路:

第一种:建立一个新矩阵,设立两个虚点(相当于原图中的虚边)使两个虚点分别与所有从出发点出发的边、所有到终点结束的边联通,用这个矩阵与刚才的矩阵相乘,统计两个虚点之间的方案数输出。
第二种:直接统计所有从出发点出发的边、所有到终点结束的边之间的方案数求和输出。

个人感觉后一种更方便一点,但是前一种也一定要会。

这题也有一个巨坑:

题目中说了有重边,所以再次理解一下题意:‘他不会立刻沿着刚刚走来的路走回’,但是他可以沿着刚刚走来的路的重边走回……

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define mod 45989
#define int LL
#define LL long long
using namespace std;
vector<int> ed[50];
int en[200],st[200];
int ni[200];
struct jz
{
	LL m[200][200];
}cs;
int n,m,t,a,b;
jz operator * (jz a,jz b)
{
	jz ans;memset(ans.m,0,sizeof(ans.m));
	for(int i=1;i<=m*2;i++)
		for(int j=1;j<=m*2;j++)
			for(int k=1;k<=m*2;k++)
				ans.m[i][j]=(ans.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
	return ans;
}
jz operator ^ (jz a,int b)
{
	jz ans=a;b--;
	while(b)
	{
		if(b&1)ans=ans*a;
		a=a*a;
		b=b>>1;
	}
	return ans;
}
signed main()
{
//	freopen("in.txt","r",stdin);

	cin>>n>>m>>t>>a>>b;a++,b++;
	int ta,tb;
	for(int i=1;i<=m;i++)	
	{
		cin>>ta>>tb;
		ta++,tb++;
		en[i]=tb;st[i]=ta;
		en[i+m]=ta,st[i+m]=tb;
		ni[i]=i+m,ni[i+m]=i;
		ed[ta].push_back(i);
		ed[tb].push_back(i+m);
	}
	for(int i=1;i<=m*2;i++)
	{
		for(int j=0;j<ed[en[i]].size();j++)
		if(ni[i]!=ed[en[i]][j])
			cs.m[i][ed[en[i]][j]]=1;
	}
	cs=cs^(t-1);
	LL cnt=0;
	for(int i=0;i<ed[a].size();i++)
		for(int j=1;j<=m*2;j++)
			if(en[j]==b)
				cnt=(cnt+cs.m[ed[a][i]][j])%mod;
	cout<<cnt%mod<<endl;
}

 

 

 

posted @ 2019-07-18 06:33  Al_Ca  阅读(246)  评论(0编辑  收藏  举报
ヾ(≧O≦)〃嗷~