洛谷 P5025 炸彈

2022-03-12 12:53:07 字數 3333 閱讀 5146

洛谷又關發題解入口了…………………………

題意見洛谷。

不難想到建圖,將每個炸彈連向所有它能炸到的炸彈,然後在這個有向圖上處理。

先來考慮怎麼處理。不難發現,每個scc內的節點的最終能炸到的炸彈集合是相等的。於是我們跑一遍tarjan,然後縮點。這時候變成了乙個dag,我們只需要求出每個scc對應的炸彈集合大小,即它能夠到達的非虛擬節點節點的數量即可。定義每個scc的權值為它內部的非虛擬節點節點的數量,那麼要求的就是縮點之後每個點能夠到達的所有點的點權之和。考慮dp,\(dp_i=\sum\limits_dp_j\)。然後交到洛谷裡,wa了前兩個點;交到loj,ac。wtf??

看了神魚的題解才醒悟過來,這樣dp會算重(原資料太水了,洛谷加了hack資料)。根據她的題解,上面說的那個問題是個世紀難題。那怎麼辦呢?不難發現這裡有特殊性質:每個炸彈最終能炸到的炸彈集合是個區間!這個證明實在是太簡單了。於是我們可以維護每個scc能到達的區間(的左端點和右端點),然後dp求這兩個端點即可。這樣不是\(\sum\)了,而是\(\min/\max\)了,就不存在重不重的問題了。

於是時間複雜度與邊數成正比。

接下來考慮如何建圖使得邊數比較小。你可能會說,這也太套路了……線段樹優化即可。由於這裡是單點連向區間,只需要維護一棵虛擬節點線段樹。

時空複雜度都是\(\mathrm o\!\left(n\log n\right)\)。常數比較大,把vector鄰接表改成鏈式前向星即可不開o2 ac。有乙個奇怪的現象,如果用vector,我們不是要開\(\mathrm o(n\log n)\)個vector嘛,這樣會導致機子很卡,輸出答案之後還要停一會兒才結束程式。可能是因為vector空間實在太大了。於是就有了乙個經驗:下次遇到點數/邊數比較大的圖論問題,盡量用鏈式前向星。說起來,今年省選day2t2也是這個問題,如果改成鏈式前向星大概率就ac了,可惜現場我想當然了,以為linux虛擬機器裡跑過就可以了,就懶得改了。當時幾天後發現這個,後悔死………………

**:

#includeusing namespace std;

#define pb push_back

typedef long long ll;

const int inf=0x3f3f3f3f,mod=1000000007;

void read(ll &x)

const int n=500000,now=n<<2,m=n*20;

int n;

struct bomba[n+1];

bool operator<(bomb x,bomb y)

void init()

void ae(int l,int r,int v,int p=1)

}segt;

int dfn[now+1],low[now+1],nowdfn;

int stk[now],top;

bool ins[now+1];

vector> scc;

int cid[now+1];

void dfs(int x)

if(dfn[x]==low[x]) }}

addedge cnei;

int dp_l[now],dp_r[now];

int main()))-a,r=upper_bound(a+1,a+n+1,bomb())-1-a;

segt.ae(l,r,i);

} for(int i=1;i<=now;i++)if(!dfn[i])dfs(i);//tarjan

cnei.init();

for(int i=1;i<=now;i++)for(int j=nei.head[i];j;j=nei.nxt[j])

int ans=0;

for(int i=0;i&v=scc[i];

// printf("scc#%d=",i);for(int j=0;j然而這題真的真的一臉有線性複雜度做法的樣子。因為在dp出錯然後發現性質的那個時候,就已經暗示了這題有特殊性質,不是一般的區間連邊,有可能能做到線性。

想連出邊沒什麼前途。不妨反過來想,盯著乙個點的連向它的入邊(其實在算貢獻的題目中,這個思想就是換乙個貢獻體)。注意到,能向它連入邊的點的**範圍都能覆蓋它。而對於它左邊,顯然那些能連入邊的點都覆蓋最右邊那個點;右邊類似。於是我們只需要對於每個點,讓左右兩側最靠近它的能連向它的點連向它即可,這樣正確性可以用「能到達」的傳遞性證。

現在邊數複雜度\(\mathrm o(n)\)了,我們想努力把連邊也做到\(\mathrm o(n)\),這樣總時空複雜度就是\(\mathrm o(n)\)了。難點在於如何快速找到最靠近的能連向它的點。以左邊為例,可以從左往右掃瞄,任意時刻顯然選越後被掃瞄到的越好。而每個點的**範圍又是乙個區間,一旦不能被它**到,以後的點都不能了。很自然地想到單調棧。

**(在之前的**上魔改的):

#includeusing namespace std;

#define pb push_back

typedef long long ll;

const int inf=0x3f3f3f3f,mod=1000000007;

void read(ll &x)

const int n=500000,m=n<<1;

int n;

struct bomba[n+1];

bool operator<(bomb x,bomb y)

if(dfn[x]==low[x]) }}

addedge cnei;

int dp_l[n],dp_r[n];

int main()

top=0;

for(int i=1;i<=n;i++)if(!dfn[i])dfs(i);//tarjan

cnei.init();

for(int i=1;i<=n;i++)for(int j=nei.head[i];j;j=nei.nxt[j])

int ans=0;

for(int i=0;i&v=scc[i];

// printf("scc#%d=",i);for(int j=0;jint sum=0;

for(int j=0;jdp_l[i]=min(dp_l[i],v[j]),dp_r[i]=max(dp_r[i],v[j]),(sum+=v[j])%=mod;

for(int j=cnei.head[i];j;j=cnei.nxt[j])

// printf("dp=%d\n",dp[i]);

(ans+=1ll*(dp_r[i]-dp_l[i]+1)*sum%mod)%=mod;

} cout

}

洛谷 P2280 雷射炸彈

題意 一種新型的雷射炸彈,可以摧毀乙個邊長為 m mm 的正方形內的所有目標。現在地圖上有 n nn 個目標,用整數 xi,yix i,y i xi yi 表示目標在地圖上的位置,每個目標都有乙個價值 v iv i vi 雷射炸彈的投放是通過衛星定位的,但其有乙個缺點,就是其爆破範圍,即那個邊長為 ...

洛谷P2280 HNOI2003 雷射炸彈

題目描述 輸入輸出格式 輸入格式 輸入檔名為input.txt 輸入檔案的第一行為正整數n和正整數r,接下來的n行每行有3個正整數,分別表示 xi,yi vi 輸出格式 輸出檔名為output.txt 輸出檔案僅有乙個正整數,表示一顆炸彈最多能炸掉地圖上總價值為多少的目標 結果不會超過32767 輸...

洛谷P2280 HNOI2003 雷射炸彈

輸入格式 輸入檔名為input.txt 輸入檔案的第一行為正整數n和正整數r,接下來的n行每行有3個正整數,分別表示 xi,yi vi 輸出格式 輸出檔名為output.txt 輸出檔案僅有乙個正整數,表示一顆炸彈最多能炸掉地圖上總價值為多少的目標 結果不會超過32767 輸入樣例 1 2 1 0 ...