快速矩阵幂HDU2254

题意:

一个有向图,两个顶点间可能有多条边,视为不同的路径,问从v1到v2之间的路径长度大小在[t1,t2]的方案数mod 2008。

输入n条边,p1,p2。因为p1,p2<2^32。但是总的顶点数不超过30.所以进行离散化即可。而且有可能t1>t2,所以要判断一下。

输入的v1,v2可能也不在有向图内。


分析:

先进行顶点的离散化,每次遇到相同的边就+1,然后输入v1,v2,t1,t2.判断v1,v2是否在图内,不在则直接输出0.

然后判断t1,t2的大小。如果t1>t2,就交换下。如果此时t2仍然等于0,就是t1=0,t2=0,直接输出0,即可。


然后利矩阵进行快速求解(A^1+A^2+....A^ t ),然后再(A^1+A^2+...A^t2)-(A^1+A^2+....A^(t1-1))得到(A^t1+A^(t1+1)+....A^t2).

/*//
快速矩阵幂 HDU 2254
//*/
#include<cstdio>
#include<map>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

typedef long long LL;
int mod=2008,n;

struct Matrix
{
    int m[32][32];
	void clear()
	{
		memset(m,0,sizeof(m));
	}
}E, Z;

Matrix Mut(Matrix A, Matrix B)//矩阵相乘
{
    Matrix ans;
    for (int i = 1; i<= n; i++)
        for (int j = 1; j<= n; j++)
        {
            ans.m[i][j] = 0;
            for (int k =1; k<= n; k++)
            {
                ans.m[i][j] += ((A.m[i][k])*(B.m[k][j]));
                ans.m[i][j] %= mod;
            }
        }
    return ans;
}
Matrix Add(Matrix A, Matrix B)//矩阵相加
{
    Matrix ans;
    for (int i = 1; i<= n; i++)
        for (int j = 1; j<= n; j++)
            ans.m[i][j] = (A.m[i][j] + B.m[i][j]) % mod;
    return ans;
}
Matrix Pow(Matrix A, LL b)//矩阵的幂
{
    Matrix t = A, ans = E;
    while (b)
    {
        if (b % 2) ans = Mut(ans, t);
        b /= 2;
        t = Mut(t, t);
    }
    return ans;
}

Matrix solve(Matrix A, int b)//矩阵的幂之和
{
    if (b == 0) 
		return E;
    else if (b == 1) 
		return A;
    else if (b == 2) 
		return Add(A, Mut(A, A));
    else if (b % 2 == 1) 
		return Add(Pow(A, b), solve(A, b - 1));
    else 
    {
        Matrix ans = solve(A, b / 2);
        Matrix C = Pow(A, b / 2);
        Matrix B = Mut(ans, C);
        return Add(B, ans);
    }
}
map<LL,int>s;
int main()
{
	// freopen("in.txt", "r", stdin);

	Matrix A,ans1,ans2;
	LL v1,v2,x,y;
	int t1,t2,q,m;
   while(scanf("%d",&m)!=EOF)
   { 
		n=0;
		s.clear();A.clear();
		for(int i=0;i<m;i++)
		{
			scanf("%I64d %I64d",&x,&y);
			if(s[x]==0||s[y]==0)//离散化处理
			{
				if(s[x]==0)
					s[x]=++n;
				if(s[y]==0)
					s[y]=++n;
			}	
			A.m[s[x]][s[y]]++;
		}

		for (int i = 1; i<= n; i++)
			for (int j = 1; j<= n; j++)
				E.m[i][j] = (i == j);

		scanf("%d",&q);
		while(q--)
		{
			scanf("%I64d %I64d %d %d",
				&v1,&v2,&t1,&t2);
			
			if(t1>t2)
				swap(t1,t2);
			if(s[v1]==0||s[v2]==0)//点不在图内
			{
				printf("0\n");
				continue;
			}
			if(t2==0)//t1=0,t2=0
			{
				printf("0\n");
				continue;
			}
			int a1=s[v1],a2=s[v2];
			 if(t1 > 1)
             {
                 ans1=solve(A, t2);
				 ans2=solve(A, t1-1);
				 printf("%d\n",(ans1.m[a1][a2]-ans2.m[a1][a2] + mod) % mod);
            }
             else
             {
                 ans1=solve(A, t2);
				 printf("%d\n", ((ans1.m[a1][a2] % mod) + mod) % mod);
             }
		}
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值