BZOJ1875[SDOI 2009] HH去散步

一道很玄学的矩阵乘法,平时我们构造的矩阵都是以点为基础,但是这样构造不能减去走过去再回来的情况,所以这道题用了一个非常玄学的以边为基础构造矩阵,构造矩阵时将一条无向边看成两条有向边,用了一条,另一条就不再用,用这个矩阵连乘t-1次,然后与与起点相连的边构造的矩阵相乘,最后统计与终点相连的边的值

update 18-07-26

发一份结构体版本的

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=45989;
const int M=250;
struct mtix
{
	int x[M][M];
	mtix(){memset(x,0,sizeof(x));}
}f,g;
int n,m,st,ed,t,cnt=2;
int v[M],head[M],nxt[M];
inline int read()
{
	int x=0;char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
inline void add(int a,int b)
{
	nxt[cnt]=head[a];v[cnt]=b;head[a]=cnt++;
	return ;
}
mtix mul(mtix a,mtix b)
{
	mtix c;
	for (int i=0;i<cnt;i++)
	for (int j=0;j<cnt;j++)
	for (int k=0;k<cnt;k++)
	c.x[i][j]=(c.x[i][j]+a.x[i][k]*b.x[k][j])%mod;
	return c;
}
void mpow(int y)
{
	mtix ans;
	for (int i=0;i<=cnt;i++) ans.x[i][i]=1;
	for (;y;g=mul(g,g),y>>=1)
		if (y&1) ans=mul(ans,g);
	f=mul(f,ans);
	int emm=0;
	for (int i=2;i<cnt;i++)
	if (v[i]==ed) emm=(emm+f.x[0][i])%mod; 
	cout<<emm;
	return ;
}
int main()
{
	n=read();m=read();t=read();st=read();ed=read();
	for (int i=1;i<=m;i++)
	{
		int a=read(),b=read();
		add(a,b);add(b,a);
	}
	for (int i=head[st];i;i=nxt[i]) f.x[0][i]=1; 
	for (int i=2;i<cnt;i++)
    	for (int k=head[v[i]];k;k=nxt[k])
		if (i!=(k^1)) g.x[i][k]=1;
	mpow(t-1);
	return 0;
}

数组版本的 

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=45989;
const int M=250;
int n,m,st,ed,t,cnt=2;
int a[M][M],b[M][M],c[M][M],e[M][M];
int v[M],head[M],nxt[M];
inline int read()
{
	int x=0;char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
inline void add(int a,int b)
{
	nxt[cnt]=head[a];
	v[cnt]=b;head[a]=cnt++;
	return ;
}
inline void mul()
{
	int d[M][M]={0};
	for (int i=0;i<cnt;i++)
	for (int j=0;j<cnt;j++)
	for (int k=0;k<cnt;k++)
	d[i][j]=(d[i][j]+c[i][k]*b[k][j])%mod;
	memcpy(c,d,sizeof(d));
	return ;
}
inline void muls()
{
	int d[M][M]={0};
	for (int i=0;i<cnt;i++)
	for (int j=0;j<cnt;j++)
	for (int k=0;k<cnt;k++)
	d[i][j]=(d[i][j]+b[i][k]*b[k][j])%mod;
	memcpy(b,d,sizeof(d));
	return ;
}
inline void mut()
{
	int d[M][M]={0};
	for (int i=0;i<cnt;i++)
	for (int j=0;j<cnt;j++)
	for (int k=0;k<cnt;k++)
	d[i][j]=(d[i][j]+a[i][k]*c[k][j])%mod;
	memcpy(e,d,sizeof(d));
	return ;
}
int main()
{
	n=read();m=read();t=read();st=read();ed=read();
	for (int i=1;i<=m;i++)
	{
		int a=read(),b=read();
		add(a,b);add(b,a);
	}
	for (int i=head[st];i;i=nxt[i]) a[0][i]=1;//超级源点到所有点? 
	for (int i=2;i<cnt;i++)
    for (int k=head[v[i]];k;k=nxt[k])
	if (i!=(k^1)) b[i][k]=1;//不是同一条边再构造 
	for (int i=0;i<=cnt;i++) c[i][i]=1;
	int y=t-1;
	for (;y;muls(),y>>=1) if (y&1) mul();//连乘 
	mut();int emm=0;//与起点连着的	
	for (int i=2;i<cnt;i++)
	if (v[i]==ed) emm=(emm+e[0][i])%mod;//统计方案 
	cout<<emm;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值