Educational Codeforces Round 40 (Rated for Div. 2) F. Runner's Problem 前缀和求区间覆盖 矩阵快速幂优化DP 离散化

题目链接
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 + 7.
题意
给出一个3行m列的矩阵 小人起点在二行一列 中间有m个 占据第ai 行 [l,r] 的障碍物 问你到(2,m) 的方案数有多少。
解题思路:
1. 首先就应该很容易的想到一个dp的递推式。
2. 观察m 发现m巨大 所以肯定是要log级别的复杂度 想到 快速幂优化 O(klog(m/k))
3. 观察得知 障碍物数量与m的最大长度相差巨大, 想到离散化优化空间。O(klog(k))
4. 观察得到障碍物之间可能出现重叠,想到前缀和求区间覆盖。
5. 离散化后 矩阵长度缩减为2*k 达到可以暴力遍历的程度了。遍历矩阵 当每次障碍物覆盖发生变化时 就用矩阵快速幂求一遍当前位置的方案数 然后修改矩阵。总复杂度应该是O(klog(m/k)) 跑了31ms 不清楚那些跑700ms是用什么方法写的。。。
想到方法很容易。。。。 找BUG找了一天。。。 太菜了
重写一遍应该能精简很多,不想写了 写吐了。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int MOD=1e9+7;
const int matX = 10;
const int mod = 1e9 + 7;
struct Matrix {
    LL n, m, s[matX][matX];
    Matrix(int n, int m): n(n), m(m) {
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) s[i][j] = 0;
        }
    }
    Matrix operator*(const Matrix &P)const {
        Matrix ret(n, P.m);
        for(int i = 0; i < n; i++) {
            for(int k = 0; k < m; k++) {
                if(s[i][k]) {
                    for(int j = 0; j < P.m; j++) {
                        ret.s[i][j] = ((LL)s[i][k] * P.s[k][j] + ret.s[i][j]) % mod;
                    }
                }
            }
        }
        return ret;
    }
    Matrix operator^(const LL &P)const {
        LL num = P;
        Matrix ret(n, m), tmp = *this;
        for(int i = 0; i < n; i++) ret.s[i][i] = 1;
        while(num) {
            if(num & 1) ret = ret * tmp;
            tmp = tmp * tmp;
            num >>= 1;
        }
        return ret;
    }
};

const int MAX=1e4+10;
class Edge {
public:
    LL l,r,a;
};
Edge edge[MAX];
int maps[5][MAX<<2];
vector<LL> disper;
void print(Matrix &b) {
    for(int i=0; i<3; i++) {
        for(int j=0; j<3; j++) {
            cout<<b.s[i][j]<<" ";
        }
        puts("");
    }
    puts("");
}
int find_key(LL x) {
    return lower_bound(disper.begin(),disper.end(),x)-disper.begin();
}
int str[MAX];
bool check(int i){
    if(maps[1][i]==0&&maps[1][i-1]!=0){
        return 0;
    }
    if(maps[1][i]!=0&&maps[1][i-1]==0){
        return 0;
    }
    if(maps[2][i]==0&&maps[2][i-1]!=0){
        return 0;
    }
    if(maps[2][i]!=0&&maps[2][i-1]==0){
        return 0;
    }
    if(maps[3][i]==0&&maps[3][i-1]!=0){
        return 0;
    }
    if(maps[3][i]!=0&&maps[3][i-1]==0){
        return 0;
    }
    return 1;
}
int main() {
    LL n,m;
    scanf("%lld %lld",&n,&m);
    for(int i=0; i<n; i++) {
        scanf("%lld %lld %lld",&edge[i].a,&edge[i].l,&edge[i].r);
        disper.push_back(edge[i].l);
        disper.push_back(edge[i].r+1);
    }
    disper.push_back(1);
    disper.push_back(m);
    sort(disper.begin(),disper.end());
    disper.erase(unique(disper.begin(),disper.end()),disper.end());
    // 离散化
    for(int i=0; i<n; i++) {
        maps[edge[i].a][find_key(edge[i].l)]++;
        maps[edge[i].a][find_key(edge[i].r+1)]--;
    }
    for(int i=1; i<disper.size(); i++) {
        for(int j=1; j<=3; j++) {
            maps[j][i]+=maps[j][i-1];
        }
    }
    // 前缀和求区间覆盖
    Matrix base(3,3);
    base.s[0][0]=1,base.s[0][1]=1,base.s[0][2]=0;
    base.s[1][0]=1,base.s[1][1]=1,base.s[1][2]=1;
    base.s[2][0]=0,base.s[2][1]=1,base.s[2][2]=1;
    int per=0;
    Matrix ans(3,3);
    ans.s[0][0]=0;
    ans.s[1][0]=1;
    ans.s[2][0]=0;
    // 矩阵初始化
    for(int i=1; i<disper.size(); i++) {
        bool flag=0;
        //print(ans);
        if(!check(i)||disper[i]==m) {
            //cout<<disper[i]<<"fuck"<<disper[per]<<endl;
            ans=(base^(disper[i]-disper[per]-1))*ans;
            //print(ans);
            per=i;
            flag=1;
        }
        if(maps[1][i]>0)
            base.s[0][0]=base.s[0][1]=base.s[0][2]=0;
        else if(maps[1][i]==0)
            base.s[0][0]=1,base.s[0][1]=1,base.s[0][2]=0;
        if(maps[2][i]>0)
            base.s[1][0]=base.s[1][1]=base.s[1][2]=0;
        else if(maps[2][i]==0)
            base.s[1][0]=1,base.s[1][1]=1,base.s[1][2]=1;
        if(maps[3][i]>0)
            base.s[2][0]=base.s[2][1]=base.s[2][2]=0;
        else if(maps[2][i]==0)
            base.s[2][0]=0,base.s[2][1]=1,base.s[2][2]=1;
        if(flag) {
            ans=base*ans;
        }
    }
    cout<<ans.s[1][0]<<endl;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值