多米诺骨牌
描述
有n个多米诺骨牌,从左到右排列,每一个骨牌都有一个高度Li,向右推倒,它会直接向右倒下,如下图,倒下后该骨牌的顶端落在Xi+Li的位置,(Xi是它位于的坐标,即倒下时该骨牌不会发生移动)
在倒下过程中,骨牌会碰到其他骨牌,碰到的骨牌会向右倒,如下图,最左边的骨牌倒下会碰倒A,B,C,A,B,C会倒下,但是不会直接碰到D,但是D会因为C的倒下而碰倒。
现在给你N个骨牌的坐标Xi,和每个骨牌的高度Li。则一个骨牌能碰倒另一个骨牌当切仅当xi+li≥xj。同时有Q个询问 [L,R],问向右推到第L个骨牌,最少需要多少代价让R倒下。你可以临时增加某个骨牌的高度,增加1个高度的代价是1.
输入
第一行是一个整数N,表示共N个骨牌
接下来N行描述每个骨牌的信息Xi,Li表示每个骨牌的位置与高度,保证Xi<Xi+1
接下来一行是一个整数Q,表示Q个询问
接下来是Q行,表示每个询问L,R,保证1<=L<R<=N
输出
对于每个询问,分Q行打印答案
样例输入
6
1 5
3 3
4 4
9 2
10 1
12 1
4
1 2
2 4
2 5
2 6
样例输出
0
1
1
2
提示
数据规模
20%数据:N,Q<=1000,Xi<=10000
40%数据:N,Q<=10000,Xi<=100000
100%数据:2<=N<=100000,1<=Q<=200000,Xi<=10^9
题意理解
这道题暴力是很容易拿到40分的(因为数据水了)
但蒟蒻竟然连题目意思都搞错了,最后抱了个0鸭蛋回家
“问向右推到第L个骨牌,最少需要多少代价让R倒下。” 若按我的理解就是从1开始推到L,然后问还需要多少代价会使R倒下
但实际上是只把L推倒,所以个人意见把题改为“问向右推倒第L个骨牌,最少需要多少代价让R倒下。”更清楚些
分析
由于数据范围较大,特别是询问有200000次,如果每次都现求的话,肯定不现实,所以我们肯定需要什么东西进行预处理,然后O(1)查询
我们可以把题目转化为 求某一个区间内没有被覆盖到的长度是多少 然后用后缀和进行处理
首先定义 sum [ i ] 表示第 i 个骨牌到最后一个骨牌之间,不能被覆盖到的长度
然后可以利用并查集的思想,把中间没有断点(也就是说前一个骨牌倒下可以推倒这一个)的骨牌看作是一个整体,保留这个整体最左边的点
那么询问时,对于第二个骨牌,我们就去寻找他的祖先(他所在整体的最左边的骨牌),( i 表示第一个骨牌,j 表示第二个骨牌 ) 然后sum [ i ] - sum [ f i n d (j) ]就是答案了
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#define N 100009
using namespace std;
int n,Q;
struct node{
int l,r;
}a[N],b[2*N];
int fa[N];
inline int read(){
char ch;int f=1;
while((ch=getchar())<'0'||ch>'9')
if(ch=='-') f=-1;
int res=0;
while(ch>='0'&&ch<='9'){
res=res*10+ch-'0';
ch=getchar();
}
return f*res;
}
vector<int> q[N];
stack<int> S;
long long ans[2*N],sum[N];
int find(int x){//并查集找祖先的函数
if(x!=fa[x]) fa[x]=find(fa[x]);
//记住一句话:如果我不是我自己的爸爸,那我的爸爸就是我爸爸的爸爸
return fa[x];
}
void solve(int x,int id){
int temp=b[id].r;
ans[id]=sum[x]-sum[find(temp)];//后缀和
}
int main(){
n=read();
int i,j,k;
for(i=1;i<=n;++i) fa[i]=i;
for(i=1;i<=n;++i){
a[i].l=read();
a[i].r=a[i].l+read();//第i个骨牌的左端点,以及能到达的右端点
}
Q=read();
for(i=1;i<=Q;++i){//对于数据范围,可以从循环里去检验(这就是为什么b数组是 2*n 的原因了)
b[i].l=read();
b[i].r=read();
q[b[i].l].push_back(i);
}
for(i=n;i>=1;--i){
while(!S.empty()&&a[i].r>=a[S.top()].l){//如果属于一个整体
fa[find(S.top())]=i;
a[i].r=max(a[S.top()].r,a[i].r);
S.pop();
}
if(!S.empty()) sum[i]=sum[S.top()]+a[S.top()].l-a[i].r;//如果中间有断点,就更新
else sum[i]=0;
S.push(i);//用栈来存储
int len=q[i].size();
for(j=0;j<len;++j){
solve(i,q[i][j]);
}
}
for(i=1;i<=Q;++i)
printf("%lld\n",ans[i]);
return 0;
}