[2018雅礼集训1-8]送你一堆区间 线段树优化DP

23 篇文章 0 订阅

题面
其实做着题有点受海明距离那题启发,状态都设计的差不多。
给关键点离散化后,把每个区间重新定位,并把左端点按从小到大排序。设 fi,j 表示只考虑前 i 个区间,前j个关键点都覆盖到了,假设第 i 个区间为[li,ri]那么有转移:
fi,j=fi1,j
fi,ri+=li1krifi1,k
fi,k+=fi1,k(k>ri)
因为第一个转移(行与行之间的复制关系)所以可以用线段树维护,第二个转移就是一个区间求和和单点修改,第三个转移就是区间乘了。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define up(x,y) x=mmm((x)+(y))
#define hc(x,y) x=(!(y||x)?0:x*y%mod)
using namespace std;
const int maxn=500010;
const int mod=1000000009;
int n,m,tim=0,l[maxn],r[maxn],z[maxn],key[maxn],len;
bool b[maxn];
int read()
{
    int x=0;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;
}
struct edge
{
    int r;
    edge *next;
}*con[maxn];
void ins(int l,int r)
{
    edge *p=new edge;
    p->r=r;
    p->next=con[l];
    con[l]=p;
}
inline ll mmm(ll x)
{
    return x>=mod?x-mod:x;
}
struct tree
{
    ll sum,mul;
    tree *ls,*rs;
    tree(){sum=0;mul=1;ls=rs=NULL;}
    void update(){sum=mmm(ls->sum+rs->sum);}
    void cal(ll d){hc(sum,d);}
    void pushdown(){if(mul!=1) ls->cal(mul),rs->cal(mul),hc(ls->mul,mul),hc(rs->mul,mul),mul=1;}
    void build(int l,int r)
    {
        if(l==r) return ;
        int mid=(l+r)>>1;
        (ls=new tree)->build(l,mid);
        (rs=new tree)->build(mid+1,r);
    }
    void add(int pl,ll d,int l,int r)
    {
        if(l==r) {up(sum,d);return;}
        pushdown();
        int mid=(l+r)>>1;
        if(pl<=mid) ls->add(pl,d,l,mid);
        else rs->add(pl,d,mid+1,r);
        update();
    }
    void rmul(int lx,int rx,ll d,int l,int r)
    {
        if(lx>rx) return ;
        if(l==lx&&r==rx) {cal(d);hc(mul,d);return;}
        pushdown();
        int mid=(l+r)>>1;
        if(rx<=mid) ls->rmul(lx,rx,d,l,mid);
        else if(lx>mid) rs->rmul(lx,rx,d,mid+1,r);
        else ls->rmul(lx,mid,d,l,mid),rs->rmul(mid+1,rx,d,mid+1,r); 
        update();
    }
    ll qsum(int lx,int rx,int l,int r)
    {
        if(l==lx&&r==rx) return sum;
        pushdown();
        int mid=(l+r)>>1;
        if(rx<=mid) return ls->qsum(lx,rx,l,mid);
        else if(lx>mid) return rs->qsum(lx,rx,mid+1,r);
        else return mmm(ls->qsum(lx,mid,l,mid)+rs->qsum(mid+1,rx,mid+1,r));
    }
}*xtr;
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
        l[i]=read(),r[i]=read();
    for(int i=1;i<=m;i++)
        key[i]=read(),z[i]=key[i];
    sort(z+1,z+m+1);
    len=unique(z+1,z+m+1)-z-1;
z,ins(l[i],r[i]);
    int mxkey=0;
    for(int i=1;i<=m;i++)
        key[i]=lower_bound(z+1,z+len+1,key[i])-z,b[key[i]]=1,mxkey=max(mxkey,key[i]);
    for(int i=1;i<=n;i++)
        l[i]=lower_bound(z+1,z+len+1,l[i])-z,r[i]=upper_bound(z+1,z+len+1,r[i])-z-1,ins(l[i],r[i]); 
    (xtr=new tree)->build(0,len);   
    xtr->add(0,1,0,len);
    for(int i=1;i<=len;i++)
        for(edge *p=con[i];p;p=p->next)
        {
            xtr->add(p->r,xtr->qsum(i-1,p->r,0,len),0,len);
            xtr->rmul(p->r+1,len,2ll,0,len);
        }
    printf("%lld",xtr->qsum(mxkey,len,0,len));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值