洛谷 P2671 求和

2021-10-03 03:08:01 字數 4452 閱讀 7119

想要oi學得好,數學肯定不能少 看來我是永遠也學不好了

此篇博文較長 廢話較多 ,您可以選擇**

寫在最前面:其實不需要用 long long 型別,只需在兩數相乘時先取模一次就行了(為什麼可行,請參見取模的百科 又是該死的數學知識 )

在下面的**中,既有用 long long 的,又有用 int 的,也是因為當時並不知道這一點。

依照題意,模擬。三重迴圈列舉 x,y,z,複雜度約為 o(n^3)

**略(預估20~30左右)

.不難發現,其實三元組與中間的y沒有關係。所以,我們只需列舉 x 與 z 即可,複雜度約為 o(n^2) 。附**:(40分)

#include

using

namespace std;

int n,m;

int ans;

struct nodea[

100007];

intread()

while

(ch>=

'0'&&ch<=

'9')

if(flag)

return

-res;

else

return res;

}int

main()

}}cout

}

考慮優化(其實考場上寫乙個暴力就行了)。思考:既然三元組與中間的 y 其實並沒有關係,而三元組又需要顏色相同,那麼為什麼不按照顏色來分排序呢?

首先按照顏色的遞增順序排序,然後依次判斷。判斷時要注意顏色相同,序號的間隔要大於1(這樣才有三元)

排序演算法為 o(nlogn),判斷部分看上去為 o(n^2),實則大大減小。因為一種顏色不可能非常多,所以內部迴圈的次數相對較少(如果資料裡出現某種顏色特別多,也沒有辦法了,出題人這就太毒瘤了)。附**:(70分)

#include

#include

#include

#include

using

namespace std;

long

long n,m;

long

long ans;

struct nodea[

100007];

//其實也不需要 long long

bool

cmp(node x,node y)

intread()

while

(ch>=

'0'&&ch<=

'9')

if(flag)

return

-res;

else

return res;

}int

main()

} cout

}

這是乙個不太理解具體原因的優化:把 if 語句改為 continue 語句後,可以加快執行速度。附**:(80分)

for

(int i=

1;i<=n-1;

++i)

}

5.還可以繼續優化。發現:其實排序是多餘的,我們只要用乙個二維陣列,第一維儲存顏色,第二維儲存數字和原本的序號(結構體)。這樣就省去了排序的時間。為了防止爆記憶體,明顯不能直接定義陣列為100000*100000,所以使用stl中的vector

另:在掃瞄顏色時,不應當直接 o(n) 掃,而應當提前儲存出現過的顏色

其實可以繼續優化記憶體,通過set來自動對顏色排序,然而,因為筆者暫時無法寫出 set 與 vector 的巢狀(懶得調 bug ),所以本篇不表(倘使以後會了,如果還能想起來這篇博文,那麼再來補充一下)。

理論上來說,此程式避免了排序用時,應當更快一些,然而在實際效率中卻不盡然,不知是因**常數太大,還是因為stl太慢;但:部分資料表明,開啟o2、3系列優化後的stl效率極高,所以只是oi這方面的規定對發揮stl比較不利而已,stl相當快(不過 stl 的 sort 是真的厲害,基本是沒有人能寫過。p.s.網上有篇博文貌似手寫超過了 sort,筆者也不知道是因為測試資料還是真的手寫超過了)。

在不開o2情況下(使用cin),超時四個點,在開啟o2後,在cin的情況下,只會超時兩個點(並且乙個點是超時0.04s),而奇怪的是,若改用 scanf,則超時四個點。若使用快讀,則只會超時乙個點。

附**:(cin版本)

#include

using

namespace std;

int m,n;

struct nodea[

100007];

vector s[

100007];

//相當於二維陣列

int vis[

100007];

//存放出現過的顏色,其實也可以用vector

int tot;

int ans;

intmain()

for(

int i=

1;i<=n;

++i)

for(

int i=

1;i<=tot;

++i)}}

cout

}

那麼,如何ac呢?筆者並不會(不愧是我 ),採用的是第一篇題解的方法。由於該篇題解說明較少,估計會有人看不懂推導的過程 其實是我這個弱智 ,這裡做一些更具體、更易懂的解釋。

其方法如下:按顏色分組,再按其數字的奇偶性分組(即同組的元的序號奇偶性相同)。

設第 i 個分組裡有 k 個元,其數字為 x[1],x[2],x[3]……x[k],其原來的序號為 y[1],y[2],y[3]……y[k]。

ans=

(x[1]+x[2])*(y[1]+y[2])+(x[1]+x[3])*(y[1]+y[3])+……+(x[1]+x[k])*(y[1]+y[k])

+(x[2]+x[3])*(y[2]+y[3])+(x[2]+x[4])*(y[2]+y[4])+……+(x[2]+x[k])*(y[2]+y[k])

……+(x[k-1]+x[k])*(y[k-1]+y[k])

//觀察到,第一行有 x[1]*(n-1)*y[1],第二行有 x[2]*(n-2)*[y2],第三行有 x[3]*(n-3)*y[3]……

//但不要忘了前面的行。第一行有 1*x[2]*y[2],1*x[3]*y[3]……第二行有 1*x[3]*y[3]……

//所以,對於第 j 個元,有 x[j]*(n-1)*(y[j])

//觀察到,第一行有 x[1]*(y[2]+y[3]+……+y[k]),第二行有 x[2]*(y[3]+……+y[k])……

//但不要忘了前面的行。第一行有 x[2]*[y1],x[3]*y[1]……第二行有 x[3]*y[2]……

//所以,對於第 j 個元,有 x[j]*(y[1]+y[2]+……+y[k]),但其中沒有y[j]

//所以,考慮從 n-1 個 y[j] 中拿出來乙個,使得變為:

//對於第 j 個元,有 x[j]*(n-2)*(y[j])+x[j]*(y[1]+y[2]+……+y[k]),這次其中就包括 y[j] 了

//所以,可以將 y[1]+y[2]+y[3]+……+y[k]提前算出,然後就大大降低複雜度

附**:

#include

using

namespace std;

const

int wzr=

10007

;//某位同學,就當沒看見(wzr我估計你也不會看見的)

int n,m,ans;

int lat_num[

100007

],per_num[

100007][

2],sum[

100007][

2],clo[

100007];

// 元的數字 每種顏色,每種奇偶性的個數 每種顏色,每種奇偶性的累加和(即y[1]+y[2]+y[3]+……+y[k])

intmain()

for(

int i=

1;i<=n;

++i)

printf

("%d\n"

,ans)

;return

0;}

附:關於 continue 語句和 if 語句執行速度的解釋:在某**筆者發布了討論,一名熱心提供了自己的測試結果為 continue 快,而筆者的測試結果為 if 快。所以,推測可能是由於評測機的波動造成的。

洛谷P2671 求和 數論

題目傳送門 格式難調,題面就不放了。分析 zyys 的一道題。很顯然是大力推公式。我們分析一下題目,實際上限制條件就是 下標同奇偶且顏色相同的數,那麼我們先拿這個公式 x z num x num z 套三個變數 x,y,z 推一下 x z num x num z num x x num z z nu...

洛谷 P2671 求和 解題報告

一條狹長的紙帶被均勻劃分出了 n 個格仔,格仔編號從 1 到 n 每個格仔上都染了一種顏色 color i 用 1,m 當中的乙個整數表示 並且寫了乙個數字 number i 定義一種特殊的三元組 x,y,z 其中 x,y,z 都代表紙帶上格仔的編號,這裡的三元組要求滿足以下兩個條件 xyz 是整數...

NOIP2015PJ 洛谷 P2671 求和

直接暴力肯定是不行的,我們需要把各種相同的顏色分離出來計算。但是只分離出來沒有用,因為三元組要求中間有乙個y。可以看出,x和z的奇偶性相同時,存在y使三元組成立。所以我們用兩個陣列儲存相同顏色的奇 偶情況,每次計算就變成計算這個序列的值。則原式 num 1 num 2 a num 1 a num 2...