ZOJ 3538 Arrange the Schedule / 矩阵快速幂

老师开的DP专题 说题目不难 我看了这题 n很大 以前做过很多n很大的 然后是矩阵快速幂加速的DP

DP方程很好推 打了一下草稿 2*2的01矩阵

要是没有m个限制那就是水题了 m最多才10 我想了下 可以分成m+1段矩阵快速幂来求 遇到那几个限制的天数的时候就单独用一个矩阵乘一下

这样的题目做的很少 写了几个小时 最后1A 题目不算难 细心点 矩阵构造很简单 纸上写一下

如果第x天必须是B 矩阵是

0 0 0 0

1 0 1 1

0 0 0 0

0 0 0 0

如果x天没要求

0 1 1 1

1 0 1 1

1 1 0 1

1 1 1 1

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod = 1000000007;
struct node
{
	int day;
	char str[10];
}a[12];
struct Mat
{
    long long a[5][5];
};
Mat A, B;
Mat get(Mat x, Mat y)
{
    Mat z;
    memset(z.a, 0, sizeof(z.a));
    for(int i = 1; i <= 4; i++)
        for(int j = 1; j <= 4; j++)
            for(int k = 1; k <= 4; k++)
            {
                z.a[i][j] += x.a[i][k]*y.a[k][j];
                z.a[i][j] %= mod;
            }
    return z;
}
void Mat_pow(int n)
{
	//puts("s");
	if(n <= 0)
		return;
    while(n)
    {
        if(n&1)
            B = get(A, B);
        A = get(A, A);
        n >>= 1;
    }
}
bool cmp(node a, node b)
{
	return a. day < b.day;
}

void init()
{
	for(int i = 1; i <= 4; i++)
	{
		for(int j = 1; j <= 4; j++)
		{
			if(i == j)
				A.a[i][j] = 0;
			else
				A.a[i][j] = 1;
		}
	}
}
void init2(int k)
{
	memset(A.a, 0, sizeof(A.a));
	int p = a[k].str[0] - 'A' + 1;
	for(int i = 1; i <= 4; i++)
	{
		if(i == p)
			continue;
		A.a[p][i] = 1;
	}
}
int main()
{
	int n, m;
	long long in[5];
	while(scanf("%d %d", &n, &m) != EOF)
	{
		for(int i = 0; i < m; i++)
		{
			scanf("%d %s", &a[i].day, a[i].str);
		}
		sort(a, a+m, cmp);

		memset(B.a, 0, sizeof(B.a));
		for(int i = 1; i <= 4; i++)
			B.a[i][i] = 1;
		int curr = 1;
		in[1] = in[2] = in[3] = in[4] = 1;
		init();
		for(int i = 0; i < m; i++)
		{
			init();
			int d = a[i].day;
			if(d == 1)
			{
				memset(in, 0, sizeof(in));
				in[a[i].str[0]-'A'+1] = 1;
				//printf("%d\n", in[1]);
				continue;
			}
			//printf("%d\n", d-curr-1);
			Mat_pow(d-curr-1);
			curr = d;
			init2(i);
			B = get(A, B);
		}
		init();
		if(curr < n)
			Mat_pow(n-curr);
		long long ans = 0;
		for(int i = 1; i <= 4; i++)
		{
			for(int j = 1; j <= 4; j++)
			{
				ans += B.a[i][j]*in[j];
				//printf("%lld %lld ", B.a[i][j], in[j]);
				ans %= mod;
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}
/*
1 1
1 A

2 1
1 A

1000000000 0
647919529

1000000000 1
1 A
411979884

1000000000 1
100 A

1000000000 2
1 A
100 A
417773600
*/


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值