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

来源:互联网 发布:精准扶贫数据平台网址 编辑:程序博客网 时间:2024/05/29 03:02

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
PrevSubmitStatus Statistics Discuss Next
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), groupCh 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 21 A3 C2 11 D

Sample Output

23

Hint

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

Source

ZOJ Monthly, September 2011

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;}