codeforces 720D. Slalom

41 篇文章 0 订阅
26 篇文章 0 订阅

细节炸的好惨…

根据只能向上或者向右走的性质,我们考虑一种朴素的做法
令f[i][j]表示走到第i列,第j行的路径数
这样做时间复杂度是 O(nm) 的,而且会算重复一些路径,因为对于每个障碍的位置关系都相同的路径是视为同一路径的
先考虑怎么去重
每条路径,除非需要绕过障碍,否则我们都让他保持最低的高度,即贴着底线走,由于其只能向上或右走的性质,别的走法能走到的地方这样走也都能走到,且在这种约束下, 对每个障碍的位置关系相同的路径会收束到一条,就可以做到去重
然后考虑优化复杂度
我们用扫描线从左到右扫过去
如果没有遇到障碍,每条路径都沿着原来的高度走,f[i+1][j]=f[i][j],我们不需要做任何操作
如果遇到了一个障碍,它对在他上方的路径不会有影响,在他下方的路径此时可以选择是否走这个障碍的左边,假设他的高度在l~r,如果r+1此时没有被其他障碍覆盖,记l下面的最高的障碍高度为k,那么高度在k+1~l-1的这些路径可以选择走这个障碍的左边,线段树兹瓷区间求和给r+1加上去,然后把l~r这段高度的路径数量清空

复杂度就变成了 O(n+klogm) ,可以通过此题

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 1100000;
const int maxk = 110000;
const ll Mod = 1e9+7;

int n,m,k;
struct segment
{
    ll c,flag;
}seg[maxn<<2];
void pushdown(const int x,const int l,const int r)
{
    if(seg[x].flag!=-1)
    {
        int mid=l+r>>1,lc=x<<1,rc=lc|1;
        ll fl=seg[x].flag%Mod; seg[x].flag=-1;

        seg[lc].c=(ll)(mid-l+1)*fl%Mod;
        seg[lc].flag=fl;

        seg[rc].c=(ll)(r-mid)*fl%Mod;
        seg[rc].flag=fl;
    }
}
void pushup(const int x)
{
    int lc=x<<1,rc=lc|1;
    seg[x].c=(seg[lc].c+seg[rc].c)%Mod;
}
int lx,rx;
ll v;
void upd(const int x,const int l,const int r)
{
    if(rx<l||r<lx) return;
    if(lx<=l&&r<=rx)
    {
        seg[x].c=(ll)(r-l+1)*v%Mod;
        seg[x].flag=v;
        return;
    }
    pushdown(x,l,r);
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    upd(lc,l,mid); upd(rc,mid+1,r);
    pushup(x);
}
void add(const int x,const int l,const int r)
{
    if(l==r) { seg[x].c+=v; return; }
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    pushdown(x,l,r);
    if(lx<=mid) add(lc,l,mid);
    else add(rc,mid+1,r);
    pushup(x);
}
void cal(const int x,const int l,const int r)
{
    if(rx<l||r<lx) return;
    if(lx<=l&&r<=rx) { v+=seg[x].c; return; }
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    pushdown(x,l,r);
    cal(lc,l,mid); cal(rc,mid+1,r);
}

struct D
{
    int pos,x1,x2,i;
    D(){}
    D(const int _pos,const int _x1,const int _x2,const int _i){pos=_pos;x1=_x1;x2=_x2;i=_i;}
}a[maxk<<1]; int cnt;
inline bool cmp(const D x,const D y)
{
    if(x.pos==y.pos) return x.i==y.i?x.x1>y.x1:x.i>y.i;
    return x.pos<y.pos;
}

multiset<int>S;
multiset<int>::iterator it;

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=k;i++)
    {
        int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        a[++cnt]=D(x1,y1,y2,1);
        a[++cnt]=D(x2+1,y1,y2,-1);
    }sort(a+1,a+cnt+1,cmp);

    lx=rx=1; v=1ll; upd(1,1,m);
    int pos=1; S.insert(0); S.insert(m+1);
    while(pos<=cnt&&a[pos].pos==1)
    {
        S.insert(a[pos].x1); S.insert(a[pos].x2);
        pos++;
    }
    for(int i=2;i<=n;i++)
    {
        while(pos<=cnt&&a[pos].pos==i)
        {
            if(a[pos].i==1)
            {
                it=S.upper_bound(a[pos].x2);
                it--;
                lx=(*it)+1,rx=a[pos].x2; v=0ll;
                if(lx<=rx) cal(1,1,m); v%=Mod;
                it=S.upper_bound(a[pos].x2);
                if((*it)!=a[pos].x2+1) lx=rx=a[pos].x2+1,add(1,1,m);
                lx=a[pos].x1,rx=a[pos].x2,v=0ll,upd(1,1,m);
                S.insert(a[pos].x1); S.insert(a[pos].x2);
            }
            else
            {
                it=S.find(a[pos].x1);
                S.erase(it);
                it=S.find(a[pos].x2);
                S.erase(it);
            }
            pos++;
        }
    }
    it=S.lower_bound(m); it--;
    lx=(*it)+1,rx=m,v=0;
    if(lx<=rx) cal(1,1,m);
    printf("%I64d\n",v%Mod);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值