經典的O3優化(一般寫在開頭)
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
G++手動擴大棧(寫在main的開始)
int size = 256 << 20; // 256MB
char *p = (char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p));
C++手動擴大棧(一般比較少起作用,推薦上面那個)
#pragma comment(linker, "/STACK:102400000,102400000")
G++ 64位手動擴大棧
extern int main2(void) __asm__ ("main2");
int main2(){ exit(0); }//在這里寫main函數
int main(){
int size=64<<20; char *p=(char*)malloc(size)+size;
__asm__ __volatile__("movq %0, %%rsp\n" "pushq $exit\n" "jmp main2\n" :: "r"(p));
}
讀入優化
inline int rd(int& X){
char ch=X=0;
for(;ch<'0' || ch>'9';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar()) X=(X<<3)+(X<<1)+ch-'0';
return X;
}
更快的讀入優化
char str[10000001]; int stl=0;
inline int rd(int& X){
char ch=X=0;
for(;ch<'0' || ch>'9';ch=str[stl++]);
for(;ch>='0'&&ch<='9';ch=str[stl++]) X=(X<<3)+(X<<1)+ch-'0';
return X;
}
int main(){
fread(str,10000000,1,stdin);
}
如果輸入文件較大以上方法可以把ch=str[stl++]改成自己實現的getchar,具體代碼如下
#define SIZE 1000000
char str[SIZE]; int stl=SIZE;
inline char getch(){
return stl==SIZE?fread(str,SIZE,1,stdin),str[stl=0]:str[stl++];
}
inline int rd(int& X){
char ch=X=0;
for(;ch<'0' || ch>'9';ch=getch());
for(;ch>='0'&&ch<='9';ch=getch()) X=(X<<3)+(X<<1)+ch-'0';
return X;
}
線段樹和樹狀數組記得使用位運算
ST表
不要用math庫的log!自己預處理!
對於s[i][j]=f(s[i][j-1],s[i+(1<<j-1)][j-1]),我們將i,j的順序調換一下可以提高效率
特別是在二維ST表效果顯著,速度提升四倍有多
取模
首先不得不說把模數寫成變量是不太好的做法。。。
我們可以
#define M 1000000007
const int M=1000000007
如果題目要求輸入模數呢?
我們還是可以
int main(){
int p; scanf("%d",&p);
const int Mod=p;
}
如果只有加法,我們考慮判定性取模即可
int mod(int x){ return x>Mod?x-Mod:x; }
空間優化,記得C++有一個不常用的東西:short
在64位機器上少用指針因為它和long long一樣費空間
對於一些奇怪的64位的OJ,我們可以使用__int128來過一些毒瘤數論題(比如對10^18取模)
關於memset和memcpy以及memmove
這幾個函數效率都非常高,比循環的速度快很多
其實用法也很簡單,比如原code是這樣的
for(int i=l;i<=r;++i) a[i]=0;
for(int i=l;i<=r;++i) a[i]=b[i];
for(int i=l;l<=r;++i) a[i]=b[i],b[i]=0;
我們可以改成
memset(a+l,0,r-l+1<<2);
memcpy(a+l,b+l,r-l+1<<2);
memmove(a+l,b+l,r-l+1<<2);
注意,左移的位數和a,b的類型有關,這里默認為int,sizeof(int)=4,所以就是左移2(乘4)
如果不知道a,b類型所占字節數,可以改成如下
memset(a+l,0,(r-l+1)*sizeof(a[0]));
memcpy(a+l,b+l,(r-l+1)*sizeof(a[0]));
memmove(a+l,b+l,(r-l+1)*sizeof(a[0]));
關於Dijkstra算法
一般的教材(誤)上面都說:只適用於所有邊權為正的情況
而實際上,dijk+heap是可以在負權圖上面跑的,而且比spfa還好寫
具體實現,去掉vis數組就好了,雖然這樣效率可能比不上spfa了(因為有一個log)
關於負權回路和spfa
如果題目就是要你找一個負環,我們可以直接將queue<int>改成priority_queue<int>
這樣通常效率會提高很多(僅限於找負環,跑一般的最短路會慢)
如果還想更快,就把priority_queue<int> 的比較函數變成以下的函數(自己實現一個類似greater<int>的東西)dist是距離
inline bool cmp(int a,int b){ return dist[a]>dist[b]; }
數據結構
- 在維護樹上路徑時,如果只是點的獨立的加減,可以考慮用括號序來維護(拆成兩部分)
- 需要求樹上很多路徑中k近/距離和 一類,考慮點分治/在點分樹上解決。
- 子樹求和可以轉化為DFS序上區間求和
- 樹狀數組可以區間查詢/修改(差分)
- 需要查詢序列上區間數據結構,只要滿足總和是可以接受的范圍,可以用線段樹,每個區間維護一個這樣的數據結構(例如AC自動機等)
- 多維偏序問題,排序可以降維,CDQ分治可以降維,剩下只需要樹狀數組/線段樹
- 樹上連通塊有概率出現,再加上和的次方,往往可以拆開來,變成任意選K個可重,有序的點,考慮貢獻。
- 當又需要分塊,每個塊維護數據結構時,塊的大小考慮調整(不再一定是\(\sqrt n\))(平衡規划)
- 同理,對於圖論中度數總和固定、多組詢問查詢的點數固定,查詢點需要枚舉出邊,但可能一直查詢度數比較大的點。此時考慮平衡規划。(詢問點個數/度數 大於/小於\(\sqrt n\)分開來做)
- 對於點分治時兩個不同的子樹的結果混在一起需要判掉,可以考慮幾種辦法:
- 在點分樹上兒子記錄當前點分樹子樹中的節點到父親的結果,計算父親時在這里減去。
- 維護DFS序,兩個子樹對應兩個無交的區間,可以考慮區間分裂一類的做法。
- 對於有很多顏色的點,需要對相同顏色計算影響,可以把每個顏色拉出來在DFS序上搞事情(相鄰+1,lca-1一類)
- 如果又加上了深度限制,那么相當於除了DFS序這一維,還多出了深度這一維,可以考慮(主席樹/CDQ分治/二維數據結構)
- 對於這樣一類問題:每個元素(邊/點之類)具有權值/權值范圍,每次只需要考慮權值是一定值/一定范圍的元素的影響,可以考慮建立權值線段樹,將元素的影響掛在線段樹對應的所有區間上,查詢就查詢區間。
- 當需要查詢樹上是否存在一條路徑過兩個點時,可以將路徑端點記在DFS序上,然后兩點子樹查詢,這就變成了兩個區間數點的問題(二維偏序/掃描線/DFS動態樹狀數組維護增量)
- 需要維護序列輪轉問題時,不一定非要splay,如果輪轉很特殊時可以采用線段樹+預留空位的形式轉化為單點修改。
- 想要存儲很多東西的0/1狀態,且需要支持xor/or/and等操作時,bitset是個非常好的選擇(計算復雜度可以除以32),別忘了bitset還有左移右移操作,可以用來處理+或-
- 替罪羊樹跑的很快。
- 帶旋轉的平衡樹是很難在內層套上線段樹的,所以平衡樹套線段樹應考慮替罪羊樹或treap
- 一堆操作+詢問的題,如果很容易處理一堆操作對一堆詢問的貢獻,可以考慮分治,你可以考慮權值/時間分治
- 一棵Trie如果要維護+1異或,那么不妨從低位到高位建Trie
- 線段樹分治往往應用在一些對象知道插入和刪除時間時,維護合法情況很容易,但撤銷非法情況比較困難時。
- 要算一個點和一堆點的距離的時候,可以考慮將距離拆成兩點深度和-2*lca深度,lca深度可以表示成lca到根的節點數,那么直接樹鏈剖分鏈上區間加區間求和即可。
圖論
- 求點雙連通分量棧中仍然可以存點,圓方樹維護起來很方便。
- 無向圖中最大值最小的路徑一定在最小生成樹上
- 合並兩個連通塊的直徑,直接比較四個端點兩兩連起來的長度即可。
- 一些有代價的完美覆蓋問題,選格子有行列限制有代價/收益的題往往考慮網絡流。
多項式
- 碰到諸如\(\prod\limits_{i=1}^{n}(1+p^ix)\)的時候,先不急着分治NTT,它既可以多項式ln+exp,又可以倍增。顯然倍增更快。
其他
- 如果遇到\(n^3\)的轉移矩陣,但是我們一次只想知道的結果是一維的(即暴力乘的復雜度是\(n^2\)),那么可以考慮倍增預處理轉移矩陣的冪,求出轉移矩陣\(2^0,2^1,2^2...\)次的結果。詢問的時候只需要\(n^2\log\)而不是\(n^3\log\),預處理則是\(n^3\log\),總的復雜度就可以變成\(q*n^2\log+n^3\log\)
- DP時,如果狀態很大,結果很小,可以考慮能否將結果與狀態互換。
- 涉及網格圖帶權,行列選擇限制/覆蓋一類的問題,可以考慮網絡流。
- 一個經典問題:有一個序列,給出若干個區間,問有多少種選法使得選出的區間能覆蓋整個序列。 我們考慮容斥,顯然容斥系數是(-1)^強制不覆蓋的位置個數,記f[i]為前i個位置都已經確定了,第i個位置不選的方案數和,它可以從\(f[j]*(-1)* 2^k\)轉移而來,我們把所有區間掛在右端點,從左到右掃的時候做區間乘法即可。
援引
https://www.cnblogs.com/BAJimH/p/10569411.html
https://blog.csdn.net/JacaJava/article/details/78336840?utm_source=blogxgwz5