Educational Codeforces Round 40 F. Runner's Problem

Educational Codeforces Round 40 F. Runner's Problem

题意:

给一个$ 3 * m \(的矩阵,问从\)(2,1)$ 出发 走到 \((2,m)\) 的方案数 \(mod 1e9 + 7\), 走的规则和限制如下:

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.

给出了$n $个限制 每个限制如下描述

\(a_i, l_i, r_i l_i <= r_i 1<=a_i <= 3\) 表示第\((a_i, l_i)\)\((a_i, ri)\) 都是不可走的

\(n <= 10000 , m <= 10^{18}\)

思路:

考察没有限制的情况, 写出转移矩阵做快速幂即可

\(\begin{bmatrix} 1& 1 &0 \\ 1& 1 &1 \\ 0& 1 &1 \end{bmatrix}\)

那么给定了限制之后,其实就是转移矩阵在某一段内不会发生变化,处理出每一段做快速幂即可。

最开始我处理每一段的方法有点傻逼,将所有的端点按左开右闭的方式排序,然后对于取出的每一段区间判断第1,2,3行在这段区间内是否有障碍,我采用了对每一行的障碍排序,再用指针的方式来判断是否有障碍。

#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
using namespace std;
const int mod = 1e9 + 7;
vector<pair<LL,LL> > a[3];
vector<pair<LL,int> >p;
int n, x;
LL m, l, r;
struct MAT{
    int a[3][3];
    MAT operator*(const MAT &rhs){
        MAT ans;
        memset(ans.a, 0, sizeof(ans.a));
        for(int i = 0;i < 3;i++){
            for(int j = 0;j < 3;j++){
                for(int k = 0;k < 3;k++){
                    ans.a[i][j] = (ans.a[i][j] + 1LL * a[i][k] * rhs.a[k][j] % mod) % mod;
                }
            }
        }
        return ans;
    }
    MAT operator^(LL k){
        MAT ans, A = *this;
        for(int i = 0;i < 3;i++){
            for(int j = 0;j < 3;j++) ans.a[i][j] = (i == j?1:0);
        }
        for(;k;k >>= 1,A = A * A) if(k & 1) ans = ans * A;
        return ans;
    }
}mat;

int b[3][3];
void init(){
    b[0][0] = b[0][1] = 1;
    b[1][0] = b[1][1] = b[1][2] = 1;
    b[2][1] = b[2][2] = 1;
    b[0][2] = b[2][0] = 0;
}
void gao(int row){
    for(int i = 0;i < 3;i++) mat.a[row][i] = 0;
}
int main()
{
    init();
    cin>>n>>m;
    for(int i = 0;i < n;i++){
        scanf("%d%lld%lld",&x,&l,&r);
        a[x - 1].push_back(make_pair(l,r));
        p.push_back(make_pair(l - 1, 0));
        p.push_back(make_pair(r, 1));
    }
    p.push_back(make_pair(1,0));
    p.push_back(make_pair(m, 1));
    sort(p.begin(),p.end());
    p.erase(unique(p.begin(),p.end()),p.end());

    for(int i = 0;i < 3;i++) sort(a[i].begin(),a[i].end());
    
    LL mxr[3] = {1,1,1};
    LL ans[3] = {0,1,0};
    int now[3] = {0};
    l = p[0].first;
    for(int i = 1; i  < p.size();i++){
        r = p[i].first;
        memcpy(mat.a, b, sizeof(b));
        for(int j = 0;j < 3;j++){
            int &xx = now[j];
            while(xx < a[j].size() && a[j][xx].first <= l + 1 &&
                  (mxr[j] = max(a[j][xx].second,mxr[j])) < r) xx++;
            if(xx < a[j].size() && a[j][xx].first <= l + 1 && mxr[j] >= r){
                gao(j);
            }
        }

        mat = mat ^ (r - l);
        LL tmp[3] = {0};
        for(int j = 0;j < 3;j++){
            for(int k = 0;k < 3;k++) {
                tmp[j] += 1LL * mat.a[j][k] * ans[k] % mod;
                tmp[j] %= mod;
            
        }
        memcpy(ans, tmp, sizeof(tmp));
        l = p[i].first;
    }
    cout<<ans[1]<<endl;
    return 0;
}

实际上存端点的时候 可以把该端点是起点还是终点以及在哪一行存进去,这样就可以单独每一行进行维护。

当某一行遇到一个起点后,意味着该行从这个点开始都是有障碍的,直到遇到一个终点+1 后面才没有障碍,

这样就容易判断的多。

常用的区间标记操作,只是这里一时没有将这个知识用上来,以致于采用前面的做法觉得复杂很多。

#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
using namespace std;
const int mod = 1e9 + 7;
int n, x;
LL m, l, r;
struct MAT{
    int a[3][3];
    MAT operator*(const MAT &rhs){
        MAT ans;
        memset(ans.a, 0, sizeof(ans.a));
        for(int i = 0;i < 3;i++){
            for(int j = 0;j < 3;j++){
                for(int k = 0;k < 3;k++){
                    ans.a[i][j] = (ans.a[i][j] + 1LL * a[i][k] * rhs.a[k][j] % mod) % mod;
                }
            }
        }
        return ans;
    }
    MAT operator^(LL k){
        MAT ans, A = *this;
        for(int i = 0;i < 3;i++){
            for(int j = 0;j < 3;j++) ans.a[i][j] = (i == j?1:0);
        }
        for(;k;k >>= 1,A = A * A) if(k & 1) ans = ans * A;
        return ans;
    }
};
struct node{
    LL x;
    int row, f;
    node(LL x,int row,int f):x(x),row(row),f(f){};
    bool operator<(const node&rhs)const{
        return x < rhs.x;
    }
};
vector<node> p;
int main()
{

    cin>>n>>m;
    for(int i = 0;i < n;i++){
        scanf("%d%lld%lld",&x,&l,&r);
        p.push_back(node(l, x - 1, 1));
        p.push_back(node(r + 1, x - 1, -1));
    }
    p.push_back(node(m + 1, 1, -1));
    sort(p.begin(),p.end());
    int isobstacle[4] = {0};
    MAT ans;
    for(int i = 0;i < 3;i++) for(int j = 0;j < 3;j++) ans.a[i][j] = (i == j?1:0);
    l = 1;
    for(int i = 0; i < p.size();i++){ 
        r = p[i].x;
        LL d = r - l - 1;  /// 区间左闭右开
        if(d){
            MAT mat;
            for(int i = 0;i < 3;i++){ // 初始化转移矩阵
                for(int j = 0;j < 3;j++) {
                        if(!isobstacle[i] && abs(i - j) <= 1) mat.a[i][j] = 1;
                        else mat.a[i][j] = 0;
                }
            }
            ans = (mat ^ d) * ans;
        }
        isobstacle[p[i].row] += p[i].f;
        l = r - 1;
    }
    cout<<ans.a[1][1]<<endl;
    return 0;
}

posted @ 2018-03-26 21:23  jiachinzhao  阅读(200)  评论(0编辑  收藏  举报