BZOJ5017題解SNOI2017炸彈--玄學遞推
阿新 • • 發佈:2018-07-22
name long getchar 檢查 stream etc read gis fin 則拓展,同時檢查是否更新\(rr[i]\);求\(r[i]\)類似。
題目鏈接
https://www.lydsy.com/JudgeOnline/problem.php?id=5017
分析
老師講課談到了這道題,課上想出了個連邊建圖然後亂搞的操作,被老師欽定的遞推方法槍斃了;
晚上回去做了做,好像復雜度是不對。還是學習了下此題遞推方法,感覺考場上寫這個的是抱著得部分分的心理A了這道題(話說洛谷沒有SNOI2017的題目
我們用\(l[i],r[i]\)表示\(i\)最左和最右能拓展到的炸彈編號,初始化\(l[i]=r[i]=i\),\(rr[i]\)表示\(i\)最大的爆炸半徑(因為它可能會隨著\(l[i],r[i]\)更新而更新)
然後就遞推了.求\(l[i]\),如果\(x[i]-x[l[i]-1]<=rr[i]\)
時間復雜度我也很懵,感覺應該有個均攤值,老師課上講拓展次數不會超過\(log N\)感覺不太對啊。。。
同時這題有個線段樹優化建邊+Tarjan縮點+拓撲排序後DP的方法
註意
\(l[i]\)是向左拓展,故從\(1\)遞推到\(N\);
\(r[i]\)就要從\(N\)遞推到\(1\).
代碼
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cctype> #include <cmath> #define ll long long #define ri register int using namespace std; template <class T>inline void read(T &x){ x=0;int ne=0;char c; while(!isdigit(c=getchar()))ne=c==‘-‘; x=c-48; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48; x=ne?-x:x; return ; } const int maxn=500000; const int inf=0x7fffffff; int n; ll l[maxn],r[maxn],x[maxn],rr[maxn]; ll ans=0; int main(){ read(n); for(ri i=1;i<=n;i++){ read(x[i]),read(rr[i]); l[i]=r[i]=i; } for(ri i=1;i<=n;i++){ while(l[i]>1&&x[i]-x[l[i]-1]<=rr[i]){ l[i]=l[l[i]-1],rr[i]=max(rr[i],rr[l[i]]-(x[i]-x[l[i]])); } } for(ri i=n;i>=1;i--){ while(r[i]<n&&x[r[i]+1]-x[i]<=rr[i]){ r[i]=r[r[i]+1],l[i]=min(l[i],l[r[i]]); } ans=(ans+1ll*i*(r[i]-l[i]+1))%1000000007; } printf("%lld\n",ans); return 0; }
推薦學習博客
https://blog.csdn.net/c_k_y_/article/details/79980119
https://blog.csdn.net/Icefox_zhx/article/details/78877188
BZOJ5017題解SNOI2017炸彈--玄學遞推