日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[Snoi2017]炸弹

發(fā)布時間:2024/4/11 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Snoi2017]炸弹 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

題目相關

題面:


BZOJ的題目鏈接:https://www.lydsy.com/JudgeOnline/problem.php?id=5017
LOJ的數(shù)據(jù)下載鏈接:https://loj.ac/problem/2255/testdata
一句話題意?題面那么短,自己看吧

解法

首先看到題目,容易發(fā)現(xiàn)一個性質,一個炸彈能炸到的范圍是一個區(qū)間,這個性質看起來很簡單,但是事實上非常有用,方便解題
考慮每個炸彈炸到的范圍是一個區(qū)間,也就是說求出每個炸彈炸到的區(qū)間就可以計算答案了,很容易想到的一個想法是把最左端和最右端分別求出來,但是你會發(fā)現(xiàn)炸到右邊的點可能會炸到更左邊,然后再炸到更右邊,所以無法分別計算區(qū)間左端點和區(qū)間右端點

算法1

考慮暴力算法,對于每個炸彈,向自己能炸到的炸彈連邊,對于互相能炸到的炸彈,其能炸到的區(qū)間一定是相同的,所以用tarjan進行有向圖縮點,對于剩下的這個DAG進行dp,統(tǒng)計一個點子孫的數(shù)量,f[i][j]表示第j個點是否是i的子孫,時間和空間復雜度均為O(n2)O(n^2)O(n2),所以不是很優(yōu)。考慮一個點的的子孫集合一定是一段連續(xù)的區(qū)間,所以在dp的時候記子節(jié)點的最大、最小編號即可,空間復雜度轉成O(n)O(n)O(n),此時算法瓶頸在邊數(shù)過多,考慮線段樹優(yōu)化連邊,點數(shù)仍然為O(n)O(n)O(n),邊數(shù)優(yōu)化成O(nlogn)O(nlogn)O(nlogn),就可以過了,這種算法是網(wǎng)上最常見的算法,但并不是最優(yōu)的

算法2

這道題事實上能做到O(n)O(n)O(n),先講一個并不是網(wǎng)上看到的O(n)O(n)O(n)算法,考慮連邊的時候不使用線段樹優(yōu)化連邊,而是對于每一個點找到其左側和右側分別離自己最近的能炸到自己的點,將這兩個點向自己連邊。由于連邊需要的只要先讓能直接炸到自己的點向自己連邊,容易發(fā)現(xiàn),在左邊的點如果能炸到自己,那么也一定能炸到左邊連邊的那個點,右邊同理,所以這個算法正確,由于每個點只連出2條邊,復雜度O(n)O(n)O(n)
這個算法我自己寫過驗證的,所以貼出一波代碼
總復雜度由于離散化所以是O(nlogn)O(nlogn)O(nlogn)的,但是其實可以通過基數(shù)排序來使復雜度變?yōu)?span id="ozvdkddzhkzd" class="katex--inline">O(n)O(n)O(n)優(yōu)于算法1

#include<cstdio> #include<cctype> #include<algorithm> #include<cstring> #define rg register typedef long long LL; template <typename T> inline T max(const T a,const T b){return a>b?a:b;} template <typename T> inline T min(const T a,const T b){return a<b?a:b;} template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;} template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;} template <typename T> inline T abs(const T a){return a>0?a:-a;} template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;} template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);} template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;} template <typename T> inline T square(const T x){return x*x;}; template <typename T> inline void read(T&x) {char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x; } template <typename T> void printe(const T x) {if(x>=10)printe(x/10);putchar(x%10+'0'); } template <typename T> inline void print(const T x) {if(x<0)putchar('-'),printe(-x);else printe(x); } const int maxn=500003,maxm=1000003; int n,Stack[maxn],Top; LL x[maxn],l[maxn],r[maxn]; int head[maxn],nxt[maxm],tow[maxm],tmp; int belo[maxn],n_d,MIN[maxn],MAX[maxn]; int stack[maxn],top; inline void addb(const int u,const int v) {tmp++;nxt[tmp]=head[u];head[u]=tmp;tow[tmp]=v; } int tot,DFN[maxn],LOW[maxn]; int beg; inline void push(const int x){stack[++top]=x;} inline int pop(){return stack[top--];} void DFS(int u) {DFN[u]=LOW[u]=++tot;push(u);for(rg int i=head[u];i;i=nxt[i]){const int v=tow[i];if(!DFN[v])DFS(v),mind(LOW[u],LOW[v]);else if(belo[v]==0)mind(LOW[u],DFN[v]);}if(LOW[u]==DFN[u]){n_d++;int cl=pop();MIN[n_d]=cl,MAX[n_d]=cl,belo[cl]=n_d;while(cl!=u)cl=pop(),mind(MIN[n_d],cl),maxd(MAX[n_d],cl),belo[cl]=n_d;} } int head_d[maxn],nxt_d[maxm],tow_d[maxm],tmp_d; inline void addb_d(const int u,const int v) {if(u==v)return;tmp_d++;nxt_d[tmp_d]=head_d[u];head_d[u]=tmp_d;tow_d[tmp_d]=v; } int dp[maxn]; int mini[maxn],maxi[maxn]; void draw() {for(rg int i=1;i<=n;i++)for(rg int j=head[i];j;j=nxt[j])addb_d(belo[i],belo[tow[j]]); } const LL mod=1000000007; LL res;LL ans[maxn]; void calc(const int u) {dp[u]=0;mini[u]=MIN[u],maxi[u]=MAX[u];for(rg int i=head_d[u];i;i=nxt_d[i]){const int v=tow_d[i];if(dp[v]==-1)calc(v);mind(mini[u],mini[v]),maxd(maxi[u],maxi[v]);}ans[u]=maxi[u]-mini[u]+1; } int main() { read(n);for(rg int i=1;i<=n;i++)read(x[i]),read(r[i]);for(rg int i=1;i<=n;i++){l[i]=std::lower_bound(x+1,x+n+1,x[i]-r[i])-x;r[i]=std::upper_bound(x+1,x+n+1,x[i]+r[i])-x-1;}for(rg int i=1;i<=n;i++){while(Top&&r[Stack[Top]]<i)Top--;if(Top)addb(Stack[Top],i);Stack[++Top]=i;}Top=0;for(rg int i=n;i>=1;i--){while(Top&&l[Stack[Top]]>i)Top--;if(Top)addb(Stack[Top],i);Stack[++Top]=i;}for(beg=1;beg<=n;beg++)if(!DFN[beg])DFS(beg);draw();memset(dp,-1,sizeof(dp));for(rg int i=1;i<=n_d;i++)if(dp[i]==-1)calc(i);for(rg int i=1;i<=n;i++)res=(res+(LL)i*ans[belo[i]])%mod;print(res);return 0; }

算法3(搬自https://blog.csdn.net/C_K_Y_/article/details/79980119)(假算法)

這個算法同樣是O(n)O(n)O(n)的復雜度,代碼量、常數(shù)都比算法2要優(yōu)
先拋開建圖縮點跑dp的思路,考慮之前貪心拓展的算法
為什么往兩邊分別找最遠的點求出的答案不對呢?容易發(fā)現(xiàn),你炸到的左邊的點可能炸的比你還右邊,也就是說兩邊會相互影響
而算法3通過掃兩遍的方式解決了這個問題,第一遍維護了向左拓展對向右拓展的貢獻,第二遍算出向右的最遠值并更新右對左的影響
對于復雜度,容易發(fā)現(xiàn),在每個while循環(huán)中最多循環(huán)2次,第一次是用到自己原有的數(shù)據(jù),第二次是跟著拓展出去的點更新信息,容易發(fā)現(xiàn)更新出去后已經(jīng)為最優(yōu),所以總復雜度是O(n)
然而不幸的是,這個算法雖然能過bzoj然而卻是錯的,數(shù)據(jù)

5 1 0 30 100 50 20 90 0 100 0

能叉掉這個算法
因為它每次都是向左跳一次,而這移動一格+一跳會跳過中間可能時答案更優(yōu)的點,所以本來求的所謂向左、向右最遠就是錯的
然而,對于卡一般暴力向左、右分別跳的此種數(shù)據(jù)卻能過:

4 60 0 90 20 100 10 110 50

(我一開始測了這個算法這個數(shù)據(jù),發(fā)現(xiàn)過了,bzoj上也AC了,覺得真是很優(yōu)越,然后最后卻被告知這是一個假算法,深深感受到自己看別人的算法嚴謹性不足)

總結

這道題網(wǎng)上大多是算法1,而算法2在一定程度上優(yōu)化了算法復雜度,算法3能通過此題,復雜度看起來十分優(yōu)越,但是卻是錯的(數(shù)據(jù)好水啊)。整個優(yōu)化算法的想法和思路都很有趣,所以用這篇博客來記錄一下。現(xiàn)在的很多題都有多種解法,倘若能夠優(yōu)化算法復雜度、代碼量與常數(shù),那么你將可以感受到算法的樂趣(并且加大數(shù)據(jù)范圍使題目變成毒瘤題)。

總結

以上是生活随笔為你收集整理的[Snoi2017]炸弹的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。