CodeForces-954F Runing's Problem
You are running through a rectangular field. This field can be represented as a matrix with 3 rows and m columns. (i, j) denotes a cell belonging to i-th row and j-th column.
You start in (2, 1) and have to end your path in (2, m). From the cell (i, j) you may advance to:
- (i - 1, j + 1) — only if i > 1
- (i, j + 1), or
- (i + 1, j + 1) — only if i < 3.
However, there are n obstacles blocking your path. k-th obstacle is denoted by three integers ak, lk and rk, and it forbids entering any cell (ak, j) such that lk ≤ j ≤ rk.
You have to calculate the number of different paths from (2, 1) to (2, m), and print it modulo 109
The first line contains two integers n and m (1 ≤ n ≤ 104, 3 ≤ m ≤ 1018) — the number of obstacles and the number of columns in the matrix, respectively.
Then n lines follow, each containing three integers ak, lk and rk (1 ≤ ak ≤ 3, 2 ≤ lk ≤ rk ≤ m - 1) denoting an obstacle blocking every cell (a
Print the number of different paths from (2, 1) to (2, m), taken modulo 109 + 7. If it is impossible to get from (2, 1) to (2, m), then the number of paths is 0.
ExampleinputCopy2 5 1 3 4 2 2 3outputCopy
2
題意:
有一個3×M的田野
一開始你在(1,2)位置
如果你在(i,j)位置
在不出界的前提下,可以走到(i+1,j),(i+1,j±1)
有n段障礙,障礙不能走
詢問從(1,2)到達(M,2)的方案數
n<=10^4,M<=10^18
題解:
發現m很大,很容易往矩陣快速冪的方向想
首先,如果在某一列中沒有障礙物,並且我們計算了前一列每個單元格的路徑數量,那麼我們可以通過將第i-1列中的值向量乘以下列值來得到第i列中的值 矩陣:
然後,我們可以使用二進位制冪運算來跳過O(logk)中沒有障礙物的長段,其中k是段的長度。
如果我們不得不禁止一些行,那麼我們試著修改這個矩陣。 我們需要改變的是如果第i行被禁止,則將第i行中的每個值設定為0。 所以我們可能會跳過長段,不僅如果它們不包含任何障礙物,而且如果禁止行的集合在這個段上沒有改變。
因此,解決方案如下:將整個矩陣按障礙物的端點劃分為2n + 1個分段,然後在每個分段中,禁止行的集合不會改變(所以我們可以使用快速矩陣求冪來跳過它)。
首先我們可以將 8 種 (2^3)矩陣預處理出來,剛開始cnt[3] = {0,0,0}表示三行都沒有障礙的,每次遇到障礙就有當前矩陣計算之前的列結果,再更新cnt[]數值。
/**
有一個3×M的田野
一開始你在(1,2)位置
如果你在(i,j)位置
在不出界的前提下,可以走到(i+1,j),(i+1,j±1)
有n段障礙,障礙不能走
詢問從(1,2)到達(M,2)的方案數
n<=10^4,M<=10^18
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5;
const ll mod = 1e9+7;
struct Matrix
{
ll maze[maxn][maxn];
int len;
Matrix(){
memset(maze,0,sizeof(maze));
len = 3;
}
Matrix(int lens):len(lens){
memset(maze,0,sizeof(maze));
}
void einit() {
for(int i=0;i<len;i++)
for(int j=0;j<len;j++)
maze[i][j] = (i==j);
}
void Creat(int lens)
{
len = lens;
if(len > maxn) exit(1);
for(int i=0;i<len;i++)
for(int j=0;j<len;j++)
scanf("%lld",&maze[i][j]);
}
void output()
{
for(int i=0;i<len;i++)
{
for(int j=0;j<len-1;j++)
printf("%lld ",maze[i][j]);
printf("%lld\n",maze[i][len-1]);
}
}
Matrix operator * (const Matrix &a){
Matrix ans(len);
for(int k=0;k<len;k++)
{
for(int i=0;i<len;i++) if(maze[i][k])
{
ll temp;
for(int j=0;j<len;j++) if(a.maze[k][j])
{
temp = (maze[i][k]*a.maze[k][j]) % mod;
ans.maze[i][j] = (ans.maze[i][j] + temp) % mod;
}
}
}
return ans;
}
Matrix operator ^ (const ll &b) const { /// 注意運算子號順序
Matrix ans(len),a = (*this);ans.einit();
ll t = b;
while(t) {
if(t & 1) ans = ans * a;
a = a * a;
t >>= 1;
}
return ans;
}
void operator = (const Matrix &a){
len = a.len;
for(int i=0;i<len;i++)
for(int j=0;j<len;j++)
maze[i][j] = a.maze[i][j];
}
};
Matrix A(3),M[8];
struct node {
ll l;
int x,p;
bool operator < (const node &b) const {
return l < b.l;
}
};
vector<node> store;
int cnt[3];
int n;
ll m;
int cal() {
int ans = 0;
for(int i=0;i<3;i++) {
ans |= ((cnt[i] == 0)<<i);
}
return ans;
}
int main()
{
for(int i=0;i<8;i++) {
if((i&1)) M[i].maze[0][0] = M[i].maze[0][1] = 1;
if((i&2)) M[i].maze[1][0] = M[i].maze[1][1] = M[i].maze[1][2] = 1;
if((i&4)) M[i].maze[2][1] = M[i].maze[2][2] = 1;
}
while(~scanf("%d%lld",&n,&m))
{
int x;
ll l,r;
store.clear();
A.maze[0][1] = 1;
for(int i=0;i<n;i++) {
scanf("%d%lld%lld",&x,&l,&r);
store.push_back((node){l,x-1,1});
store.push_back((node){r+1,x-1,-1});
}
sort(store.begin(),store.end());
ll las = 1;
cnt[0] = cnt[1] = cnt[2] = 0;
for(int i=0;i<(int)store.size();i++) {
ll now = store[i].l;
if(now > las) {
A = A * (M[cal()]^(now - las));
}
cnt[store[i].x] += store[i].p;
las = now;
}
A = A * (M[7]^(m - las));
printf("%lld\n",A.maze[0][1]);
}
return 0;
}