题目翻译:
有 (n) ( (n le 10^5) ) 个多米诺骨牌在一条直线上,给定他们的坐标 (x)和高度 (h) ( (x) 越大则越靠右) ,求出当第 (i) 个骨牌向右倒下时会有几个骨牌倒下。第 (i) 块骨牌能压倒的范围在 ([x_i+1,x_i+h_i-1]) 之间。
需要注意的地方:
- 输入中骨牌的 (x) 值不一定有序
- 被压倒骨牌可能也会压倒其他骨牌
思路:
直接暴力算肯定会T,二分的话因为会有重复部分所以也不好做,因此考虑动态规划。
- 先按照 (x) 坐标排正序,但枚举时要从后向前枚举 (因为后面骨牌的不会影响前面,因此先从后面算
- 每次计算 (ans_i) 时先考虑 (i) 能否压倒 (i+1),如果可以就加上 (ans_{i+1}) ,然后再看 (i) 能否压倒 (i+1) 不能压倒的第一块骨牌,可以答案就再加上那块骨牌的答案,以此类推。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<bitset>
#include<cmath>
#include<queue>
#include<map>
#include<set>
using namespace std;
int read()
{
int ans=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
return ans*f;
}
const int N=1e5+5;
int n,ans[N],last[N];
// last 存储排序后的第 i 块骨牌压不倒的第一块骨牌的位置
struct Node
{
int x,h,p; // p 用来标记问题编号
bool operator < (const Node &t)const
{ // 重载运算符,排序时按照 x 正序排列
return t.x>x;
}
}a[N];
int main()
{
n=read();
for(int i=1;i<=n;++i)
{
a[i].x=read();
a[i].h=read();
a[i].p=i;
} // 读入
sort(a+1,a+1+n); / / 排序
for(int i=1;i<=n;++i)
ans[i]=1; // 因为压倒一块骨牌时它自己一定会倒(这不是废话吗...),所以答案至少是 1
for(int i=n-1;i>=1;--i)
// 因为最后面的骨牌谁都压不到,所以直接从 n-1 开始算
{
int now=i+1; // now 存储下一次需要比较的骨牌下标,最开始为 i+1
while(now&&a[i].x+a[i].h>a[now].x)
// 如果后面还有骨牌 并且 这块骨牌能压倒下一个需要压倒骨牌
{
ans[a[i].p]+=ans[a[now].p];
// 就把答案加上下一块需要压倒的骨牌的答案
now=last[now];
// 并更新 now 为 刚压倒的这块骨牌 的首个无法压倒的骨牌的下标
}
last[i]=now; // 维护 last ,因为下标为 now 的骨牌已经无法被压倒,且它前面的骨牌都可以被压倒,所以 now 就是这块骨牌的首个无法被压倒的骨牌
}
for(int i=1;i<=n;++i)
//输出时注意按照原输入顺序来输出
printf("%d ",ans[i]);
return 0;
}