埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛H题小Y与多米诺骨牌(线段树优化dp)

题意

题目链接:https://www.nowcoder.com/acm/contest/91/H
来源:牛客网

题解

l[i]为向左推第i个骨牌最远能影响到的骨牌的编号,r[i]为向右推第i个骨牌最远能影响到的骨牌的编号,则有:

l[i]=min(l[j])+1, (j<i,x[i]y[i]<x[j])r[i]=max(r[j])+1, (i<j,x[j]<x[i]+y[i])

使用线段树预处理出lr数组,复杂度为O(nlog(n))
dp[i][0]表示第i个骨牌向左倒时,让前i个骨牌全倒的最少要推的骨牌数;dp[i][1]表示第i个骨牌向右倒时,让前i个骨牌全倒的最少要推的骨牌数。则有:
dp[i][0]=min{dp[j1][0]+1,l[i]jdp[j1][1]+1,l[i]jdp[i][1]=min{dp[j1][0]+1,r[j]idp[i1][1]+1,r[j]i

答案就是min(dp[n][0],dp[n][1]),用线段树优化dp方程,复杂度为O(nlog(n))

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
#define lx x<<1
#define rx x<<1|1
#define lson x<<1,s,mid
#define rson x<<1|1,mid+1,t
const int inf=0x3f3f3f3f;
int n;
int seg[2][N*6],lazy[2][N*6];
int x[N],y[N],l[N],r[N],dp[2][N];
inline int op(int k,int x,int y)
{
    if(k==0) return min(x,y);
    else return max(x,y);
}
void build2(int x,int s,int t)
{
    seg[0][x]=s;seg[1][x]=t;
    if(s==t)
    {
        l[s]=r[s]=s;
        return;
    }
    int mid=s+t>>1;
    build2(lson);build2(rson);
}
void updata2(int k,int x,int s,int t,int id,int w)
{
    if(s==t){seg[k][x]=w;return;}
    int mid=s+t>>1;
    if(id<=mid) updata2(k,lson,id,w);
    else updata2(k,rson,id,w);
    seg[k][x]=op(k,seg[k][lx],seg[k][rx]);
}
int query2(int k,int x,int s,int t,int l,int r)
{
    if(l<=s&&t<=r) return seg[k][x];
    int mid=s+t>>1,q=(k?0:inf);
    if(l<=mid) q=op(k,q,query2(k,lson,l,r));
    if(mid<r) q=op(k,q,query2(k,rson,l,r));
    return q;
}
inline void pushdown(int k,int x,int s,int t)
{
    if(lazy[k][x]!=inf)
    {
        if(s!=t)
        {
            seg[k][lx]=min(seg[k][lx],lazy[k][x]);
            seg[k][rx]=min(seg[k][rx],lazy[k][x]);
            lazy[k][lx]=min(lazy[k][lx],lazy[k][x]);
            lazy[k][rx]=min(lazy[k][rx],lazy[k][x]);
        }
        lazy[k][x]=inf;
    }
}
void updata(int k,int x,int s,int t,int l,int r,int w)
{
    if(l<=s&&t<=r)
    {
        seg[k][x]=min(seg[k][x],w);
        lazy[k][x]=min(lazy[k][x],w);
        return;
    }
    pushdown(k,x,s,t);
    int mid=s+t>>1;
    if(l<=mid) updata(k,lson,l,r,w);
    if(mid<r) updata(k,rson,l,r,w);
    seg[k][x]=min(seg[k][lx],seg[k][rx]);
}
int query(int k,int x,int s,int t,int l,int r)
{
    if(l<=s&&t<=r) return seg[k][x];
    pushdown(k,x,s,t);
    int mid=s+t>>1,q=inf;
    if(l<=mid) q=min(q,query(k,lson,l,r));
    if(mid<r) q=min(q,query(k,rson,l,r));
    return q;
}
int main()
{
    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
            scanf("%d%d",&x[i],&y[i]);
        build2(1,0,n);
        for(int i=2;i<=n;++i)
        {
            int k=upper_bound(x+1,x+n+1,x[i]-y[i])-x;
            l[i]=query2(0,1,0,n,k,i);
            updata2(0,1,0,n,i,l[i]);
        }
        for(int i=n-1;i>0;--i)
        {
            int k=lower_bound(x+1,x+n+1,x[i]+y[i])-x-1;
            r[i]=query2(1,1,0,n,i,k);
            updata2(1,1,0,n,i,r[i]);
        }
        memset(seg,0x3f,sizeof(seg));
        memset(lazy,0x3f,sizeof(lazy));
        dp[0][0]=0;updata(0,1,0,n,0,0,0);
        dp[1][0]=0;updata(1,1,0,n,0,0,0);
        for(int i=1;i<=n;++i)
        {
            int tmp0=query(0,1,0,n,l[i]-1,i-1);
            int tmp1=query(1,1,0,n,l[i]-1,i-1);
            dp[0][i]=min(tmp0,tmp1)+1;
            updata(0,1,0,n,i,i,dp[0][i]);
            updata(1,1,0,n,i,r[i],min(dp[0][i-1],dp[1][i-1])+1);
            dp[1][i]=query(1,1,0,n,i,i);
        }
        printf("%d\n",min(dp[0][n],dp[1][n]));
    }
    return 0;
}
/*
3
4
1 3
2 1
3 1
4 3
3
1 3
4 2
5 2
3
1 3
4 3
7 7
*/
阅读更多
文章标签: acm
个人分类: DP 题解 线段树
上一篇NCPC 2016 K.Keeping the Dogs Apart(GYM 101550K)题解
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭