想要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...