P11094 题解
DaiRuiChen007 · · 题解
Problem Link
题目大意
给定
n 棵树,位置在a_1\sim a_n ,一棵树能被砍当且仅当(a_i-h_i,a_i] 或[a_i,a_i+h_i) 中没有未被砍的树,且a_i-h_i/a_i+h_i 不能超过出[a_1,a_n] 的范围。数据范围:$n,q\le 5\times 10^5$。
思路分析
对每棵树
不难发现
我们要求的就是有多少
考虑二维数点,拆成
然后考虑如何求
- 如果
a_j+h_j\le a_i ,直接向右砍,j\gets j-1 。 - 否则只能向左砍,
j\gets L_j-1 。
那么
另一种做法是整体二分,即枚举一个左端点
容易发现
时间复杂度
代码查询
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=5e5+5;
int n,q,a[MAXN],h[MAXN],L[MAXN],R[MAXN],ans[MAXN];
vector <int> o[MAXN];
vector <array<int,2>> ql[MAXN],qr[MAXN];
struct FenwickTree {
int tr[MAXN],s;
void init() { memset(tr,0,sizeof(tr)); }
void add(int x) { for(;x;x&=x-1) ++tr[x]; }
int qry(int x) { for(s=0;x<=n;x+=x&-x) s+=tr[x]; return s; }
} T;
int f[MAXN],stk[MAXN],tp;
void solve(int l,int r,const vector<int>&id) {
if(l==r) {
for(int x:id) f[x]=l;
return ;
}
int mid=(l+r+1)>>1;
stk[tp=0]=mid-1;
vector <int> li,ri;
for(int i:id) {
if(i<mid) { li.push_back(i); continue; }
while(tp&&a[stk[tp]]+h[stk[tp]]<=a[i]) --tp;
if(a[stk[tp]]>a[i]-h[i]) li.push_back(i),stk[++tp]=i;
else ri.push_back(i);
}
solve(l,mid-1,li),solve(mid,r,ri);
}
signed main() {
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i) scanf("%d%d",&a[i],&h[i]);
vector <int> id(n);
iota(id.begin(),id.end(),1);
a[0]=a[1],solve(0,n,id);
for(int i=1;i<=n;++i) L[i]=f[i];
reverse(a+1,a+n+1),reverse(h+1,h+n+1);
for(int i=n;i;--i) a[i]*=-1;
a[0]=a[1],solve(0,n,id);
for(int i=1;i<=n;++i) R[n-i+1]=n-f[i]+1;
for(int i=1,l,r;i<=q;++i) {
scanf("%d%d",&l,&r),ql[l].push_back({r,i}),qr[r].push_back({l,i});
}
for(int i=1;i<=n;++i) o[R[i]].push_back(L[i]);
for(int i=1;i<=n;++i) {
T.add(L[i]);
for(auto z:qr[i]) ans[z[1]]+=T.qry(z[0]);
}
T.init();
for(int i=n;i>=1;--i) {
T.add(n-R[i]+1);
for(auto z:ql[i]) ans[z[1]]+=T.qry(n-z[0]+1);
}
T.init();
for(int i=1;i<=n;++i) {
for(int z:o[i]) T.add(z);
for(auto z:qr[i]) ans[z[1]]-=T.qry(z[0]);
}
for(int i=1;i<=q;++i) printf("%d\n",ans[i]);
return 0;
}