codeforces 56E 多米诺骨牌效应

BSNY正在玩多米诺骨牌,他将n块多米诺骨牌从左到右排成一行。其中每块多米诺骨牌所在的位置设为x,每块多米诺骨牌高度为h。如果将x位置上的多米诺骨牌向右翻到,它就可以影响[x+1, x+h-1]范围内的所有多米诺骨牌,让他们也翻到,同时这些被翻到的多米诺骨牌还能影响到其他多米诺骨牌,也就是多米诺骨牌效应。
例如现在有4块多米诺骨牌,位置从左到右依次为10 16 18 20,它们的高度依次为10, 5, 2, 5。当翻到16位置上的多米诺骨牌,18和20位置都在范围[16+1, 16+5-1],所以这两块也翻到了,总共翻到了3块。
现在BSNY给出n块多米诺骨牌的位置和高度,问如果向右翻到第i块多米诺骨牌,会有多少多米诺骨牌翻到。
输入
第一行输入n
接下来n行每行输入xi和hi,分别表示第i块多米诺骨牌的位置和高度
输出
输出一行包含n个结果,用空格隔开
第i个结果表示如果向右翻到第i块多米诺骨牌,会有多少多米诺骨牌翻到。
样例输入
4
16 5
20 5
10 10
18 2
样例输出
3 1 4 1
提示
【样例说明】
输入:
4
0 10
1 5
9 10
15 10
输出:
4 1 2 1
【数据规模和约定】

1<=n<=10^5,  -10^8<=xi<=10^8 ,  2<=hi<=10^8   xi都不一样

********************************************************************

solution:相信大家都会n^2的暴力,但这无疑会超时。现在我们的一个瓶颈是:怎么快速计算?那么势必要成片累加。我们可以记录下每个点往后能够连续推倒的数量,只要它在范围内,就可以累加这个值,然后直接跳过已被覆盖的点。这样就可以大大节约时间(虽然仍有一部分常数),考试的时候我也是想到这种边扩展边累加的方法,但是没想到用dp的方式来记录并累加,所以就超了。。。。。

#include<cstdio>
#include<iostream>
#include<algorithm>
#define p1 id<<1
#define p2 id<<1^1
using namespace std;
int n;
int ans[100005];
int tree[450000];
struct ty
{
    int x,h,id,to;
}d[100005];
bool cmp(ty a,ty b)
{
    return a.x<b.x;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&d[i].x,&d[i].h);
        d[i].id=i;
        d[i].to=d[i].x+d[i].h-1;
    }
    sort(d+1,d+n+1,cmp);
    ans[d[n].id]=1;
    for(int i=n-1;i>=1;i--) 
    {
        int last=d[i].to;
        int q=i+1;
        ans[d[i].id]=1;
        while(0==0)
        {
            if(q>n) break;
            if(d[q].x<=last) ans[d[i].id]+=ans[d[q].id];  //为什么不用更新last,因为我在当前点能到的范围之外的点,要是能推倒的话,肯定已经包括在当前范围里的某个点中
            else break;
            q=q+ans[d[q].id];
        }
    }   
    for(int i=1;i<n;i++) cout<<ans[i]<<' ';
    cout<<ans[n]<<endl;
    return 0;
}









评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值