1. 程式人生 > >[bzoj5017][Snoi2017]炸彈——遞推

[bzoj5017][Snoi2017]炸彈——遞推

題目大意:

在一條直線上有 N 個炸彈,每個炸彈的座標是 Xi,爆炸半徑是 Ri,當一個炸彈爆炸時,如果另一個炸彈所在位置 Xj 滿足: Xi−Ri≤Xj≤Xi+Ri,那麼,該炸彈也會被引爆。 現在,請你幫忙計算一下,先把第 i 個炸彈引爆,將引爆多少個炸彈呢?

思路:

不難發現,在引爆一個炸彈之後發生的一串連鎖反應中,只有兩種情況: 1.左邊(右邊)的炸彈依舊引爆左邊(右邊)的炸彈。 2.左邊(右邊)的炸彈引爆了原本右邊(左邊)引爆不了的炸彈。 並且第二種情況可能會在反應中反覆很多次。 假設在第二種情況中連縮反應是從a->b開始的,對於a的答案的計算,只需要從b遞推過來即可。 於是考慮先從左往右遞推處理出來每一個點左邊的所有能夠引爆的炸彈,以及引爆了左邊的炸彈之後新增加的炸彈的半徑對於右邊炸彈的影響。 不難發現第n個炸彈目前的答案就是第n個炸彈最終的答案。 然後從右往左倒著推一遍,處理出每個點右邊的範圍,這時第n-1個炸彈的答案是從第n個炸彈推過來的,又因為第n個炸彈的答案是最終的答案,只需要把第n個炸彈的答案合併到第n-1個就好了,這樣使得第n-1個炸彈的答案也是最終的正確答案了。 這樣一直遞推,一定會使得最後的答案都是正確答案。 考慮時間複雜度的計算,一個點的遞推次數取決於它的爆炸範圍內有多少個互相不可達的子塊。如果有一個點的爆炸範圍內有很多的不可達的子塊,那麼它便會和這些子塊合併,不難發現,每個點至多會被用到2次,所以複雜度線性。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj5017.in","r",stdin);
    freopen("bzoj5017.out","w",stdout);
}

template
<typename T>void read(T &_){ T __=0,mul=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')mul=-1; ch=getchar(); } while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar(); _=__*mul; } const int maxn=5e5+10; const ll mod=1e9+7; int n,l[maxn],r[maxn]; ll x[maxn],y[maxn],ans; int
main(){ // File(); read(n); REP(i,1,n)read(x[i]),read(y[i]),l[i]=r[i]=i; REP(i,1,n)while(l[i]>1 && x[i]-x[l[i]-1]<=y[i]){ y[i]=max(y[i],x[l[i]-1]+y[l[i]-1]-x[i]); //cout<<x[l[i]-1]+y[l[i]-1]-x[i]<<" "; l[i]=l[l[i]-1]; y[i]=max(y[i],x[l[i]]+y[l[i]]-x[i]); //cout<<x[l[i]]+y[l[i]]-x[i]<<endl; } DREP(i,n,1)while(r[i]<n && x[r[i]+1]-x[i]<=y[i]){ l[i]=min(l[i],l[r[i]+1]); //cout<<l[r[i]+1]<<" "; r[i]=r[r[i]+1]; l[i]=min(l[i],l[r[i]]); //cout<<l[r[i]]<<endl; } REP(i,1,n){ ans=(ans+1ll*i*(r[i]-l[i]+1)%mod)%mod; //cout<<l[i]<<" "<<r[i]<<endl; } printf("%lld\n",(ans+mod)%mod); return 0; }