Educational Codeforces Round 69 Culture Code dp+线段树

题目链接: http://codeforces.com/contest/1197/problem/E

题意:

你现在有 n n n 个俄罗斯套娃,每个套娃有一个外框大小 o u t [ i ] out[i] out[i] 和一个内框大小 i n [ i ] in[i] in[i] o u t [ i ] &gt; i n [ i ] out[i]&gt;in[i] out[i]>in[i] ,并且他们的差就是这个套娃的厚度。套娃 i i i 可以被放进套娃 j j j 当且仅当 o u t [ i ] &lt; = i n [ j ] out[i]&lt;=in[j] out[i]<=in[j] i i i 的外框比 j j j 的内框要小。一个套娃集合要求在可以嵌套的前提下无法再增加任一个新的套娃,一个套娃集合空心的值是任两个内外套娃边框的差值,即如果 i i i j j j 之内,那么这一对空心的值就是 i n [ j ] − o u t [ i ] in[j]-out[i] in[j]out[i] ,现在要你在空心的值最小的前提下找出有多少种套娃集合。

做法:

首先要把套娃按照关键字进行排序,按照一定的顺序进行遍历。然后,对于一个套娃集合,如果我们按照大小关系进行排序,那么我们最后的空心的值的和就是 i n x + ∑ i = 1 x − 1 o u t [ i ] − i n [ i ] in_x+\sum_{i=1}^{x-1}out[i]-in[i] inx+i=1x1out[i]in[i] ,即最大的套娃减去里面所有套娃的厚度。

我们的数据结构肯定是要包含两个信息的,一个是最小的差值,一个是当前的方案数。 O ( n 2 ) O(n^2) O(n2) 的做法是,对于每一个套娃 i i i ,我们去枚举所有内框大于它的套娃 j j j 然后不断的用差值去更新我当前这个套娃的数据,但是这样做的话复杂度肯定会炸。

所以我们就需要用一个数据结构来维护这个最优值,即在一个最小的 p o s ( i n [ p o s ] &gt; = o u t [ i ] ) pos(in[pos]&gt;=out[i]) pos(in[pos]>=out[i]) 下,找到 [ p o s , n ] [pos,n] [pos,n] 之中最小的空心值,并且用这个答案去维护这个线段树。如果找不到这样的 p o s pos pos,那这个套娃就只能当最大的,(注意,我们是要从后面开始做起的,这样才能保证线段树查询后方的时候有正确的值)。

这里在 p u s h U p pushUp pushUp 的时候有一个操作,因为我们要的是最小的差值,所以要把左儿子和右儿子中的那个小的值用来更新父亲,如果两个相同,那么数量就是两个的和。

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
#define mid (l+r)/2
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int maxn=200005;
const int mod=1e9+7;
const ll inf=1e9;

pll a[maxn],sum[maxn<<2];
void build(int l,int r,int rt){
    sum[rt].fi=inf; sum[rt].se=0ll;
    if(l==r) return ;
    build(l,mid,lson);
    build(mid+1,r,rson);
}
void push_up(int rt){
    sum[rt]=min(sum[lson],sum[rson]);
    if(sum[lson].fi==sum[rson].fi) sum[rt].se=(sum[lson].se+sum[rson].se)%mod;
}
void update(int l,int r,int rt,int p,ll cost,ll add){
    if(l==r){
        sum[rt].fi=cost;
        sum[rt].se=add;
        return ;
    }
    if(p<=mid) update(l,mid,lson,p,cost,add);
    else update(mid+1,r,rson,p,cost,add);
    push_up(rt);
}
pll deal(pll x,pll y){
    pll ret=min(x,y);
    if(x.fi==y.fi) ret.se=(x.se+y.se)%mod;
    return ret;
}
pll query(int l,int r,int rt,int ql,int qr){
    if(ql<=l&&r<=qr){
        return sum[rt];
    }
    pll L={inf,0},R={inf,0};
    if(ql<=mid) L=query(l,mid,lson,ql,qr);
    if(qr>mid) R=query(mid+1,r,rson,ql,qr);
    return deal(L,R);
}
int main(){
    int n; scanf("%d",&n);
    rep(i,1,n) {
        scanf("%lld%lld",&a[i].se,&a[i].fi);
    }
    sort(a+1,a+1+n);
    for(int i=n;i>=1;i--){
        int pos=lower_bound(a+1,a+1+n,(pll){a[i].se,0})-a;
        if(pos>n){
            update(1,n,1,i,a[i].fi,1);
        }
        else{
            pll ret=query(1,n,1,pos,n);
            update(1,n,1,i,ret.fi-a[i].se+a[i].fi,ret.se);
        }
    }
    pll ans=query(1,n,1,1,n);
    printf("%lld\n",ans.se);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值