[矩阵乘法][动态规划]Arrange the Schedule

Arrange the Schedule

1000ms
65536KB
This problem will be judged on ZJU. Original ID: 3538
64-bit integer IO format: %lld      Java class name: Main
Font Size:
Type:

In Summer 2011, the ZJU-ICPC Team has a n-days training schedule. ZJU-ICPC Team has been divided into 4 Group: Akiba, BiliBili, CIA, Double(Group A, B, C, D). There is a group in charge of the training problems on each day. As the ICPC Team manager, you have to decide which team is in charge of the problems on each day.But there are something you should rememeber:

1. No team is able to provide problems on two adjacent days.
2. There are m days in the Summer 2011 that which group is in charge of the problems have been decided. (e.g. Akiba provides problems on day 1, BiliBili provides problems on day 6. And thesecan not be changed)

How many ways are there to arrange the schedule? Output the answer modulo 1000000007.

Input

There are multiple test cases(less than 50).Each case contains two integers n, m (1 ≤ n ≤ 10000000, 0 ≤ m ≤ 10), which indicate the number of days in Summer 2011 and the number of days that have been decided.
Following m lines. Each line contains one integer ai and one upper letterCh ('A' ≤ Ch ≤ 'D'), indicate that on day ai (1 ≤ai ≤ n), group Ch is in charge of the problems.We guarantee that allai are distinct.There is a blank line after each input case.

Output

For each case, output a single line containing the answer, the number of the ways to arrange the schedule modulo 1000000007.

Sample Input
3 2
1 A
3 C

2 1
1 D

Sample Output
2
3
Hint
Case 1:
2 ways: ABC, ADC.
Case 2:
3 ways: DA, DB, DC.

Source

Author

Liang, Jiaxing


一开始想到的是三维的方程。

f(i,j,k) = Σ {f(i-1,k,k2)*2 (j≠k2),f(i-1,k,k2)*3(j=k2)}

k的存在是明显的冗余(前后有相同)。这样的枚举是多余的。

直观上,我们对多种情况归为两类而变得简单,其实枚举量增加了不少。

其实状态只与上一位置有关。



但是这样仍然会超时。

观察发现第一个方程是一个简单的递推,能够联想到矩阵乘法。但是如何处理后两个方程?

题目上可以看见,会用到后两个方程的位置,即开始就有字母的位置,是相当稀疏的。

而递推是连续的,因此我们大可分段计算,以预先有字母的位置为断点,把1~n断成多段。


这个矩阵,明显是存在的,因为该递推公式是多项式。

这个矩阵其实就是:

0,1,1,1

1,0,1,1

1,1,0,1

1,1,1,0

时间复杂度为O(mlgn),很快。

这个问题的初始状态的考虑有些麻烦,最好将1位置有无字母分开单独考虑,作为两个初始状态,这是因为从位置0推出1位置并不好实现。


#include <cstdio>
#include <cstring>
#include <algorithm>
using std::sort;

typedef long long ui;

struct node
{
	int p;
	int c;
	bool operator<(const node& n2)const
	{
		return p < n2.p;
	}
};

const ui MOD = 1000000007;

void multi1(ui a[6][6],ui b[6][6],ui c[6][6])
{
	ui tmp[6][6];
	memset(tmp,0,sizeof tmp);
	for (int i=1;i<=4;i++)
	{
		for (int j=1;j<=4;j++)
		{
			for (int k=1;k<=4;k++)
			{
				tmp[i][j] = (tmp[i][j]+a[i][k]*b[k][j])%MOD;
			}
		}
	}
	for (int i=0;i<6;i++)
		for (int j=0;j<6;j++)
			c[i][j] = tmp[i][j];
}

void multi2(ui a[6][6],ui b[6],ui c[6])
{
	ui tmp[6];
	memset(tmp,0,sizeof tmp);
	for (int i=1;i<=4;i++)
	{
		for (int k=1;k<=4;k++)
		{
			tmp[i] = (tmp[i]+a[i][k]*b[k])%MOD;
		}
	}
	for (int i=0;i<6;i++)
		c[i] = tmp[i];
}

node ex[20];
const ui initial[6][6] = {
{0,0,0,0,0,0},
{0,1,0,0,0,0},
{0,0,1,0,0,0},
{0,0,0,1,0,0},
{0,0,0,0,1,0},
{0,0,0,0,0,0}};
const ui matrix[6][6] = {
{0,0,0,0,0,0},
{0,0,1,1,1,0},
{0,1,0,1,1,0},
{0,1,1,0,1,0},
{0,1,1,1,0,0},
{0,0,0,0,0,0}};
ui ma[6][6];
ui mb[6];

void quickpower(int k)
{
	ui ans[6][6];
	ui tmp[6][6];
	for (int i=0;i<6;i++)
		for (int j=0;j<6;j++)
		{
			ans[i][j] = initial[i][j];
			tmp[i][j] = matrix[i][j];
		}

	while (k)
	{
		if (k&1){multi1(ans,tmp,ans);}
		multi1(tmp,tmp,tmp);
		k >>= 1;
	}

	for (int i=0;i<6;i++)
		for (int j=0;j<6;j++)
			ma[i][j] = ans[i][j];
}

int n;int m;

int main()
{
	freopen("ats.in","r",stdin);
	freopen("ats.out","w",stdout);

	while (scanf("%d%d",&n,&m) == 2)
	{
		mb[0] = mb[1] = mb[2] = mb[3] = mb[4] = mb[5] = 0;
		for (int i=1;i<=m;i++)
		{
			scanf("%d",&ex[i].p);
			do
			{
				ex[i].c = getchar()-'A'+1;
			}while (ex[i].c>4 || ex[i].c<1);
		}

		m++;
		ex[m].p = n+1;
		ex[m].c = 5;
		sort(ex+1,ex+m+1);
		int i;
		if (ex[1].p == 1)
		{
			mb[ex[1].c] = 1;
			i = 2;
		}
		else
		{
			mb[1] = mb[2] = mb[3] = mb[4] = 1;
			i = 1;
			ex[0].p = 1;
		}
		for (;i<=m;i++)
		{
			quickpower(ex[i].p-ex[i-1].p-1);
			multi2(ma,mb,mb);
			int tmp = 0;
			for (int j=1;j<=4;j++)
			{
				if (j != ex[i].c)
				{
					tmp = (tmp+mb[j])%MOD;
				}
			}
			mb[1]=mb[2]=mb[3]=mb[4] = 0;
			mb[ex[i].c] = tmp;
		}
		printf("%lld\n",mb[5]);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值