rt,思路十分奇妙,首先无脑向左右拓展,在这一部分不考虑左边拓展到右边和右边拓展到左边的情况,得出答案后在往左边的区间求出相对于这个位置向右边拓展最远的,如果大于当前值向右拓展的情况就连一条边,右边往左边的同理,然后拓扑更新答案。
时间复杂度 O(nlogn)
洛谷和loj都过了,自己的对拍也过了,求证伪。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
#define pa pair<int,int>
#define mp make_pair
int n,a[500005],rr[500005],tr[2000005],f[500005],d[500005];
int ans,tr2[2000005],cnt,h[500005],arr[500005],rd[500005];
//a[i]是坐标,rr[i]是范围,tr是左边的拓展线段树,tr2是右边的拓展线段树
//f[i]是向左拓展最远的答案,d[i]是向右拓展最远的答案,h和e是拓扑的图
//rd[i]是入度,arr[i]是每个点的答案。
struct did{
int to,nxt;
}e[1000005];
pa seg[2000005],ano[2000005];
//seg是向右拓展距离的线段树,ano是向左拓展距离的线段树。
void change(int k,int l,int r,int mb,int v){
if(l==r){
tr[k]=v;
return ;
}
int mid=l+r>>1;
if(mid<mb)change(2*k+1,mid+1,r,mb,v);
else change(2*k,l,mid,mb,v);
tr[k]=min(tr[2*k],tr[2*k+1]);
}//维护只往左边走的线段树
void change2(int k,int l,int r,int mb,int v){
if(l==r){
tr2[k]=v;
return ;
}
int mid=l+r>>1;
if(mid<mb)change2(2*k+1,mid+1,r,mb,v);
else change2(2*k,l,mid,mb,v);
tr2[k]=max(tr2[2*k],tr2[2*k+1]);
}//维护只往右边走的线段树
int ask(int k,int l,int r,int ml,int mr){
if(l>=ml&&r<=mr){
return tr[k];
}
int mid=l+r>>1,res=INT_MAX;
if(mid>=ml)res=min(res,ask(2*k,l,mid,ml,mr));
if(mid<mr)res=min(res,ask(2*k+1,mid+1,r,ml,mr));
return res;
}//询问最多往左边走到哪
int ask2(int k,int l,int r,int ml,int mr){
if(l>=ml&&r<=mr){
return tr2[k];
}
int mid=l+r>>1,res=0;
if(mid>=ml)res=max(res,ask2(2*k,l,mid,ml,mr));
if(mid<mr)res=max(res,ask2(2*k+1,mid+1,r,ml,mr));
return res;
}//询问最多往右边走到哪
pa chmax(pa a,pa b){
if(a.first>b.first)return a;
if(a.first<b.first)return b;
if(a.second<b.second)return a;
return b;
}
pa chkano(pa a,pa b){
if(a.first>b.first)return b;
if(a.first<b.first)return a;
if(a.second>b.second)return a;
return b;
}
//两个比较函数
pa askseg(int k,int l,int r,int ml,int mr){
if(l>=ml&&r<=mr){
return seg[k];
}
int mid=l+r>>1;
pa res=mp(LLONG_MIN,0);
if(mid>=ml)res=askseg(2*k,l,mid,ml,mr);
if(mid<mr)res=chmax(res,askseg(2*k+1,mid+1,r,ml,mr));
return res;
}//询问一个值暴力拓展的左边最多向右走的
pa askano(int k,int l,int r,int ml,int mr){
if(l>=ml&&r<=mr){
return ano[k];
}
int mid=l+r>>1;
pa res=mp(LLONG_MIN,0);
if(mid>=ml)res=askano(2*k,l,mid,ml,mr);
if(mid<mr)res=max(res,askano(2*k+1,mid+1,r,ml,mr));
return res;
}//询问一个值暴力拓展的右边最多向左走的
void build(int k,int l,int r){
if(l==r){
seg[k]=mp(rr[l]+a[l],l);
ano[k]=mp(rr[l]-a[l],l);
return ;
}
int mid=l+r>>1;
build(2*k,l,mid);
build(2*k+1,mid+1,r);
seg[k]=chmax(seg[2*k],seg[2*k+1]);
ano[k]=max(ano[2*k],ano[2*k+1]);
}//建出每个值向左向右位置的线段树
deque<int> q;
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i],&rr[i]);
}
build(1,1,n);
for(int i=1,x;i<=n;i++){
x=lower_bound(a+1,a+n+1,a[i]-rr[i])-a;
if(x==i){
f[i]=i;
change(1,1,n,i,f[i]);
continue;
}
f[i]=ask(1,1,n,x,i-1);
change(1,1,n,i,f[i]);
}//暴力向左拓展
a[n+1]=LLONG_MAX;
for(int i=n,x;i>=1;i--){
x=upper_bound(a+1,a+n+1,a[i]+rr[i])-a-1;
if(a[x+1]==a[i]+rr[i])x++;
if(x==i){
d[i]=i;
change2(1,1,n,i,d[i]);
continue;
}
d[i]=ask2(1,1,n,i+1,x);
change2(1,1,n,i,d[i]);
}//暴力向左拓展
for(int i=1;i<=n;i++){
pa maxl=askseg(1,1,n,f[i],i),maxr=askano(1,1,n,i,d[i]);
//maxl是左边区间内向右最远的
//maxr是右边区间内向左最远的
if(d[maxl.second]>=d[i]&&maxl.second!=i){
e[++cnt].to=i;
e[cnt].nxt=h[maxl.second];
h[maxl.second]=cnt;
rd[i]++;
}
if(f[maxr.second]<=f[i]&&maxr.second!=i){
e[++cnt].to=i;
e[cnt].nxt=h[maxr.second];
h[maxr.second]=cnt;
rd[i]++;
}
//拓扑连边
arr[i]=d[i]-f[i]+1;
//初始答案
}
for(int i=1;i<=n;i++){
if(!rd[i])q.push_back(i);
}
while(!q.empty()){
int x=q.front();
q.pop_front();
for(int i=h[x],y;i;i=e[i].nxt){
y=e[i].to;
rd[y]--;
arr[y]=max(arr[y],arr[x]);
if(!rd[y])q.push_back(y);
}
}
//拓扑
for(int i=1;i<=n;i++){
ans+=arr[i]*i%mod;
ans%=mod;
}
cout<<ans;
return 0;
}