# 競賽程式模板 by: 何煦(hush) ## 基本模板 ### Default code 在code::blocks去Settings→Compiler→Other compiler options 加入編譯參數:-O2 -Wall -Wextra -DDEBUG :::spoiler template ```C++= #include<bits/stdc++.h> #include<bits/extc++.h> //#pragma GCC optimize("O2,unroll-loops,fast-math") //#pragma GCC target("sse,sse2,ssse3,sse4,avx,avx2") #ifdef DEBUG #define ERR(v,e) cerr<<#v<<": "<<(v)<<(e); #else #define ERR(v,e) ((void)0) #endif #define int long long #define itn int #define pii pair<int,int> #define fi first #define se second #define vi vector<int> #define EB emplace_back #define MP make_pair #define ALL(x) x.begin(),x.end() #define endl '\n' #define colinorz cin.tie(0);cout.tie(0);ios::sync_with_stdio(0); using namespace std; //mt19937 rnd(chrono::system_clock::now().time_since_epoch().count()); constexpr int MOD=1e9+7,MAXN=2e5+5,INF=1e9; signed main() { //colinorz; } ``` ::: ### Creat problems file :::spoiler code ```dockerfile= @echo off setlocal set n=10 for /l %%i in (1,1,%n%) do ( if not exist p%%i ( md p%%i ) if not exist p%%i\p%%i.cpp ( type nul > p%%i\p%%i.cpp ) for /l %%j in (1,1,3) do ( if not exist p%%i\%%j.txt ( type nul > p%%i\%%j.txt ) ) ) endlocal ``` ::: ### hash_template :::spoiler code ```C++ #include <bits/stdc++.h> using namespace std; signed main() { string s,all; ifstream in("zjq390.cpp"); while (getline(in,s)) { stringstream ss(s); string tmp; while (ss >> tmp) all+=tmp; cout<<s<<endl; } unsigned int h=7531357ULL; for (unsigned char c:all) h = (h^c)*775577ULL; cout << h << '\n'; } ``` ::: ### Common template pending... - Fast Power :::spoiler code ```C++= inline int fp(int a,int b,int m) { int re=1; a%=m; while (b) { if (b&1) re=re*a%m; b>>=1,a=a*a%m; } return re; } ``` ::: - Discretization(離散化) :::spoiler code ```C++= void dct(const vi &raw,vi &re,int base=1) { re.resize(raw.size()); vi a=raw; sort(ALL(a)); a.erase(unique(ALL(a)),a.end()); for (int i=0;i<re.size();i++) re[i]=lower_bound(ALL(a),raw[i])-a.begin()+base; } ``` ::: - DSU(併查集) :::spoiler code ```C++= struct DSU { vi boss,sz; DSU(int n=MAXN): boss(n+5),sz(n+5,1) { iota(ALL(boss),0); } int fnd(int x) { return (x==boss[x]?x:boss[x]=fnd(boss[x])); } bool sme(int a,int b) { return fnd(a)==fnd(b); } void join(int a,int b) { a=fnd(a), b=fnd(b); if (a==b) return; if (sz[a]>sz[b]) swap(a,b); boss[a]=b,sz[b]+=sz[a]; } }; ``` ::: ## 黑魔法 dark code ### I/O模板 :::spoiler template ```C++= #define gtc getchar//_unlocked #define ptc putchar//_unlocked inline int rd() { int x=0,st=gtc(); if (st==EOF) return INF; bool neg=0; char tmp=st; while (tmp<'0' || tmp>'9') { if (tmp=='-') neg=1; tmp=gtc(); } while ('0'<=tmp && tmp<='9') x=(x<<3)+(x<<1)+(tmp^'0'),tmp=gtc(); return neg?-x:x; } int buf[40]; inline void wt(int x,const char &ed) { if (!x) ptc('0'); else { if (x<0) x=-x,ptc('-'); int tp=0; while (x) buf[tp++]=x%10^'0',x/=10; while (tp--) ptc(buf[tp]); } ptc(ed); } #undef gtc //#undef ptc ``` ::: ### Random - 快速亂數函式(xorshift): :::spoiler code ```C++= static inline unsigned int xorshf() { static unsigned int x=1000000009, y=433494437, z=43112609; unsigned int t = x ^ (x << 16); t ^= (t >> 5); t ^= (t << 1); x = y; y = z; z = t ^ x ^ y; return z; } static inline int rnd() { return (int)(xorshf96() & 0x7fffffff); } ``` ::: ### PBDS_tree - 可在$O(\log n)$內查詢第k大的值或x第幾大 - 基本上就是set, map的爸爸 - 第二個type放`null_type`就是set,放其他是map | 功能 | 查小於 x 數量 | 查第 k 小元素(0-based) | | --- | ----- | ------------- | | 函式 | `order_of_key(x)` | `find_by_order(k)` | :::spoiler template ```C++ using namespace __gnu_pbds; template<typename T> using PBDS_tree=tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>; // order_of_key, find_by_order ``` ::: ### PBDS_trie :::spoiler template ```C++ using PBDS_trie=trie<string, null_type, trie_string_access_traits<>, pat_trie_tag,trie_prefix_search_node_update>; ``` ::: 遍歷一個前綴的所有字串 回傳是pair<Trie::iterator, Trie::iterator> :::spoiler ```C++ auto range = tre.prefix_range(str); for(auto it = range.first; it != range.second; it++) cout << *it << '\n'; ``` ::: ### rope - 可在$O(\log n)$內刪除、拼接的字串(陣列) - 基本上就是string他爸爸(但常數超大) - `using namespace __gnu_cxx;` | 功能 | 在位置k插入字串s | 從位置k刪除c個元素 | | --- | ----- | ------------- | | 函式 | `insert(k,s);` | `erase(k,c);` | ## Data Type ### Matrix :::spoiler code ```C++= struct MAT: vector<vi> { int rw=0,cl=0; MAT(int r,int c,int f=0): vector<vi>(r,vi(c,f)),rw(r),cl(c) {} MAT(const vector<vi>& v): vector<vi>(v),rw(vvi.size()),cl(at(0).size()) {} void rd() { for (int i=0;i<rw;i++) for (int j=0;j<cl;j++) cin >> at(i).at(j); } void wt() const { for (int i=0;i<rw;i++) for (int j=0;j<cl;j++) cout << at(i).at(j) << " \n"[j==cl-1]; } MAT unit(const int& sz) const { MAT re(sz,sz,0); for (int i=0;i<sz;i++) re[i][i]=1; return re; } MAT operator + (const MAT &b) const { MAT re=*this; for (int i=0;i<rw;i++) for (int j=0;j<cl;j++) re[i][j]+=b[i][j]; return re; } MAT operator - (const MAT &b) const { MAT re=*this; for (int i=0;i<rw;i++) for (int j=0;j<cl;j++) re[i][j]-=b[i][j]; return re; } MAT operator * (const MAT &b) const { const int len=b.rw; MAT re(rw,b.cl,0); for (int i=0;i<rw;i++) for (int j=0;j<b.cl;j++) for (int k=0;k<len;k++) re[i][j]=(re[i][j]+at(i).at(k)*b[k][j])%MOD; return re; } inline void operator *= (const MAT &b) { *this=(*this)*b; } void fp(int b) { MAT re=unit(size()),a=*this; while (b) { if (b&1) re*=a; a*=a, b>>=1; } *this=re; } void T() { MAT re(cl,rw); for (int i=0;i<rw;i++) for (int j=0;j<cl;j++) re[j][i]=at(i).at(j); *this=re; } }; ``` ::: ### Big Number - 日月掛長orz(大部分是抄[他的模板](https://sunmoon-template.blogspot.com/2015/01/big-interger.html)) :::spoiler code ```C++= //日月掛長orz template<typename T> inline string to_string(const T& x) { stringstream ss; return ss<<x,ss.str(); } struct BIG: vi { const static int base=1e9,wdt=log10(base); bool neg; BIG (const_iterator a,const_iterator b): vi(a,b) {} BIG (string s) { if (s.empty()) return; if (s[0]=='-') neg=1,s=s.substr(1); else neg=0; for (int i=(int)s.size()-1;i>=0;i-=wdt) { int tmp=0; for (int j=max(0LL,i-wdt+1);j<=i;j++) tmp=tmp*10+(s[j]^'0'); EB(tmp); } trim(); } template<typename T> BIG (const T &a): BIG(to_string(a)) {} BIG (): neg(0) {} void rd() { string s; cin >> s; *this=s; } void wt(const char& ed) { if (neg) cout << '-'; cout << (empty()?0:back()); for (int i=(int)size()-2;i>=0;i--) cout <<setw(wdt)<<setfill('0')<<at(i); cout << ed; } void trim() { while (!empty() && !back()) pop_back(); if (empty()) neg=0; } void carry(int _base=base) { for (int i=0;i<size();i++) { if (at(i)>=0&&at(i)<_base) continue; if (i+1==size()) EB(0); int r=at(i)%_base; if (r<0) r+=_base; at(i+1)+=(at(i)-r)/_base,at(i)=r; } } int abscmp(const BIG& b) const { if (size()>b.size()) return 1; if (size()<b.size()) return -1; for (int i=(int)size()-1;i>=0;i--) { if ((*this)[i]>b[i]) return 1; if ((*this)[i]<b[i]) return -1; } return 0; } int cmp(const BIG& b) const { if (neg!=b.neg) return neg?-1:1; return neg?-abscmp(b):abscmp(b); } bool operator < (const BIG& b) const { return cmp(b)<0; } bool operator > (const BIG& b) const { return cmp(b)>0; } bool operator <= (const BIG& b) const { return cmp(b)<=0; } bool operator >= (const BIG& b) const { return cmp(b)>=0; } bool operator == (const BIG& b) const { return !cmp(b); } BIG abs() const { BIG re=(*this); return re.neg=0, re; } BIG operator - () const { BIG re=*this; return re.neg=!re.neg, re.trim(), re; } BIG operator * (int b) const { BIG re=*this; if (b<0) re.neg=!re.neg,b=-b; for (int i=0,_cry=0,sz=re.size();i<sz||_cry;i++) { if (i==sz) re.EB(0); int sum=re[i]*b+_cry; _cry=sum/base; re[i]=sum%base; } return re.trim(), re; } BIG operator + (const BIG& b) const { if (neg) return -(-(*this)+(-b)); if (b.neg) return (*this)-(-b); BIG re=*this; if (size()<b.size()) re.resize(b.size()); for (int i=0;i<b.size();i++) re[i]+=b[i]; return re.carry(),re.trim(),re; } BIG operator - (const BIG& b) const { if (neg) return -(-(*this)-(-b)); if (b.neg) return (*this)+(-b); if (abscmp(b)<0) return -(b-(*this)); BIG re=*this; if (b.size()>size()) re.resize(b.size()); for (int i=0;i<b.size();i++) re[i]-=b[i]; return re.carry(),re.trim(),re; } BIG convert_base(int old_wdt,int new_wdt) const { vi p(max(old_wdt,new_wdt)+1,1); for (int i=1;i<p.size();i++) p[i]=p[i-1]*10; BIG re; int tmp=0,id=0; for (int i=0;i<size();i++) { tmp+=at(i)*p[id]; id+=old_wdt; while (id>=new_wdt) re.EB(tmp%p[new_wdt]), tmp/=p[new_wdt], id-=new_wdt; } return re.EB(tmp), re.trim(), re; } BIG kara(const BIG& b) const //karatsuba { BIG re; re.resize((int)size()<<1); if (size()<33) { for (int i=0;i<size();i++) for (int j=0;j<size();j++) re[i+j]+=at(i)*b[j]; return re; } int k=(int)size()>>1; BIG a1(begin(),begin()+k),a2(begin()+k,end()), b1(b.begin(),b.begin()+k),b2(b.begin()+k,b.end()); BIG a1b1=a1.kara(b1),a2b2=a2.kara(b2); for (int i=0;i<k;i++) a2[i]+=a1[i],b2[i]+=b1[i]; BIG r=a2.kara(b2); for (int i=0;i<a1b1.size();i++) r[i]-=a1b1[i]; for (int i=0;i<a2b2.size();i++) r[i]-=a2b2[i]; for (int i=0;i<r.size();i++) re[i+k]+=r[i]; for (int i=0;i<a1b1.size();i++) re[i]+=a1b1[i]; for (int i=0;i<a2b2.size();i++) re[i+(k<<1)]+=a2b2[i]; return re; } BIG operator * (const BIG& b) const { const static int mul_base=1e6,mul_wdt=log10(mul_base); BIG aa=convert_base(wdt,mul_wdt),bb=b.convert_base(wdt,mul_wdt); int sz=max(aa.size(),bb.size()); while (sz&(sz-1)) sz++; aa.resize(sz); bb.resize(sz); BIG re=aa.kara(bb); re.neg=neg!=b.neg; re.carry(mul_base); re=re.convert_base(mul_wdt,wdt); return re.trim(), re; } BIG operator / (const BIG& b) const { int norm=base/(b.back()+1); BIG x=abs()*norm,y=b.abs()*norm,q,r; q.resize(x.size()); for (int i=(int)x.size()-1;i>=0;i--) { r=r*base+x[i]; int s1=r.size()<=y.size()?0:r[y.size()]; int s2=r.size()<y.size()?0:r[y.size()-1]; int d=(s1*base+s2)/y.back(); r=r-y*d; while (r.neg) r=r+y,d--; q[i]=d; } return q.neg=neg!=b.neg, q.trim(), q; } BIG operator % (const BIG& b) const { return *this-((*this)/b)*b; } BIG fp (int b) const { BIG a=*this,re=1; while (b) { if (b&1) re=re*a; a=a*a,b>>=1; } return re; } }; //日月掛長orz ``` ::: ### Fraction :::spoiler code ```C++= inline int gcd(int a,int b) { return (b?gcd(b,a%b):a); } struct FRC: pii { FRC(): pii(0,1) {} FRC(const int& a,int b=1): pii(a,b) { rdc(); if (se<0) fi=-fi,se=-se; } void rdc() //reduce { //if (!se) fi=1e16,se=1; int gg=gcd(fi,se); fi/=gg,se/=gg; } FRC inv() const { return FRC(se,fi); } FRC operator - () const { return FRC(-fi,se); } FRC operator * (const int& b) const { return FRC(fi*b,se); } FRC operator + (const FRC& b) const { return FRC(fi*b.se+se*b.fi,se*b.se); } FRC operator - (const FRC& b) const { return *this+(-b); } FRC operator * (const FRC& b) const { return FRC(fi*b.fi,se*b.se); } FRC operator / (const FRC& b) const { return *this*b.inv(); } void operator += (const FRC& b) { *this=*this+b; } void operator -= (const FRC& b) { *this=*this-b; } void operator *= (const FRC& b) { *this=*this*b; } void operator /= (const FRC& b) { *this=*this/b; } bool operator < (const FRC& b) const { return fi*b.se<b.fi*se; } bool operator > (const FRC& b) const { return b<*this; } bool operator <= (const FRC& b) const { return fi*b.se<=b.fi*se; } bool operator >= (const FRC& b) const { return b<=*this; } friend ostream & operator << (ostream& out,const FRC& b) { out<<b.fi; if (b.se!=1) out<<'/'<<b.se; return out; } friend istream& operator>>(istream& in, FRC& b) { int f,s; in >> f; if (in.peek()=='/') in.ignore(); in >> s; return b=FRC(f, s),in; } }; ``` ::: ## Data Structure ### IMP-Treap - BST + Heap - 這裡是implicit Treap,支持區間操作 :::spoiler code ```C++= mt19937 rnd(chrono::system_clock::now().time_since_epoch().count()); struct node //max-heap { int p=rnd(),sz=1,val,mn,sum,add=0; bool rev=0; node *lc=0,*rc=0; node(int v): val(v), mn(v), sum(v) {} inline void tagadd(int k) { sum+=k*sz, val+=k, add+=k; mn+=k; } inline void tagrev() { rev^=1, swap(lc,rc); } inline void pull() { sz=1, mn=sum=val; for (auto i : {lc,rc}) if (i) sz+=i->sz, mn=min(mn,i->mn),sum+=i->sum; } inline void push() { for (auto i : {lc,rc}) if (i) { if (add) i->tagadd(add); if (rev) i->tagrev(); } add=rev=0; } }; struct TRP { node *rt; TRP(): rt(0) {} int sz(node *cur) { return cur?cur->sz:0; } void split(int k,node *&l,node *&r,node *cur) { if (!cur) return void(l=r=0); cur->push(); int lsz=sz(cur->lc); if (lsz+1<=k) l=cur, split(k-lsz-1,cur->rc,r,cur->rc); else r=cur, split(k,l,cur->lc,cur->lc); cur->pull(); } node* mrg(node *l,node *r) { if (!l || !r) return l?l:r; if (l->p < r->p) return r->push(), r->lc=mrg(l,r->lc), r->pull(), r; return l->push(), l->rc=mrg(l->rc,r), l->pull(), l; } inline void insert(int k,int v,node *&cur) { node *l,*r; split(k,l,r,cur); cur=mrg(mrg(l,new node(v)),r); } inline void insert(int k,int v) { insert(k,v,rt); } inline void update(int id,int x,node *&cur) { node *l,*tar,*r; split(id-1,l,tar,cur); split(1,tar,r,tar); cur=mrg(mrg(l,new node(x)),r); } inline void update(int id,int x) { update(id,x,rt); } inline void del(int k,node *&cur) { node *l,*tar,*r; split(k-1,l,tar,cur); split(1,tar,r,tar); cur=mrg(l,r); } inline void del(int k) { del(k,rt); } inline void add(int k,int ql,int qr,node *&cur) { node *l,*tar,*r; split(ql-1,l,tar,cur); split(qr-ql+1,tar,r,tar); tar->tagadd(k); cur=mrg(mrg(l,tar),r); } inline void add(int k,int ql,int qr) { add(k,ql,qr,rt); } inline int rmq(int ql,int qr,node *&cur) { node *tar,*l,*r; split(ql-1,l,tar,cur); split(qr-ql+1,tar,r,tar); int re=tar->mn; return cur=mrg(mrg(l,tar),r), re; } inline int rmq(int ql,int qr) { return rmq(ql,qr,rt); } inline void rev(int ql,int qr,node *&cur) { node *l,*tar,*r; split(ql-1,l,tar,cur); split(qr-ql+1,tar,r,tar); tar->tagrev(); cur=mrg(mrg(l,tar),r); } inline void rev(int ql,int qr) { rev(ql,qr,rt); } inline void rot(int k, int ql, int qr, node *&cur) { int len = qr - ql + 1; k%=len; if (!k) return; node *l, *tl, *tr, *r; split(ql - 1, l, tl, cur); split(len, tl, r, tl); split(len-k,tl,tr,tl); cur = mrg(mrg(mrg(l, tr), tl), r); } inline void rot(int k,int ql,int qr) { rot(k,ql,qr,rt); } }; ``` ::: ## 計算幾何 Computational Geometry ### 2D向量的基本操作 #### BASIC :::spoiler template ```C++= //#define pii pair<double,double> #define X first #define Y second constexpr double eps=1e-9; //向量的基本運算 pii operator+ (const pii& a,const pii& b){ return pii(a.X+b.X,a.Y+b.Y);} pii operator- (const pii& a,const pii& b){ return pii(a.X-b.X,a.Y-b.Y);} //內外積dot,cross inline int dot(const pii& a,const pii& b){ return a.X*b.X+a.Y*b.Y;} inline int cro(const pii& a,const pii& b){ return a.X*b.Y-a.Y*b.X;} //判斷正負零 inline int sgn(int a){ if (a==0) return 0;return a>0 ? 1 : -1;} //int sgn(double a){ if (abs(a)<eps) return 0;return a>0 ? 1 : -1;} //1==逆,-1==順,0==平行 inline int ori(const pii& a,const pii& b,const pii& c){ return sgn(cro(b-a,c-a));} //X座標由小到大排序 bool xcmp(const pii& a,const pii& b) { if (sgn(a.X-b.X)!=0) return sgn(a.X-b.X)<0; return sgn(a.Y-b.Y)<0; } //極角排序 inline bool is_neg(pii a){ return sgn(a.Y)==0?sgn(a.X)<0 : sgn(a.Y)<0;} bool ang_cmp(const pii& a,const pii& b) { int A=is_neg(a),B=is_neg(b); if (A!=B) return A<B; return sgn(cro(a,b))>0; } ``` ::: #### 線段相交 :::spoiler code ```C++= //is p between a and b bool btw(const pii& p,const pii& a,const pii& b) { return ori(p,a,b)==0 && sgn(dot(a-p,b-p))<=0; } //is line intersect(banana) bool ban(const pii& p1,const pii& p2,const pii& p3,const pii& p4) { if (btw(p1,p3,p4) || btw(p2,p3,p4) || btw(p3,p1,p2) || btw(p4,p1,p2)) return true; //是否互相在異側 return ori(p1,p2,p3)*ori(p1,p2,p4) <0 && ori(p3,p4,p1)*ori(p3,p4,p2) <0; } //intersect point pii ist(const pii& p1,const pii& p2,const pii& p3,const pii& p4) { int a123=cro(p2-p1,p3-p1),a124=cro(p2-p1,p4-p1); //double a123=cro(p2-p1,p3-p1),a124=cro(p2-p1,p4-p1); return (p4*a123-p3*a124)/(a123-a124); } ``` ::: #### 凸包 Convex Hull :::spoiler code ```C++= vector<pii> hull; void convex_hull(int n) { vector<pii> a(n); for (auto &i : a) cin >> i.X >> i.Y; sort(ALL(a),[](pii &a,pii& b){ return a.X==b.X?a.Y<b.Y:a.X<b.X; }); int sz=0; for (int _=0;_<2;_++) { for (auto &i : a) { while (hull.size()>=sz+2 && cro(hull.back()-*prev(hull.end(),2),i-*prev(hull.end(),2))<=0 ) hull.pop_back(); hull.EB(i); } hull.pop_back(); if (!_) reverse(ALL(a)),sz=hull.size(); } } ``` ::: ### 3D向量的基本操作 pending... :::spoiler template ```C++= ``` ::: ## 區間問題 ### 線段樹 Segment Tree 1. 單點修改 [例題連結:CSES1649](https://cses.fi/problemset/task/1649/) 題意:RMQ,維護區間最小值(**單點修改,區間查詢**) 時間複雜度:修改:$O(\log n)$, 查詢:$O(\log n)$ :::spoiler code ```C++= vector<int> seg(500000<<2,1e18); #define lc cur<<1 #define rc cur<<1|1 #define mid (l+r)>>1 void update(int cur,int l,int r,int i,int x) //[l,r) { if (l==r-1) { seg[cur]=x; return; } if (i<mid) update(lc,l,mid,i,x); else update(rc,mid,r,i,x); seg[cur]=min(seg[lc],seg[rc]); } int query(int cur,int l,int r,int ql,int qr) //[ql,qr) { if (l>=qr || r<=ql) return 1e18; if (ql<=l && r<=qr) return seg[cur]; return min(query(lc,l,mid,ql,qr),query(rc,mid,r,ql,qr)); } #undef lc #undef rc #undef mid ``` ::: 2. 區間修改萬用模板 :::spoiler template ```C++= #define lc cur<<1 #define rc cur<<1|1 #define mid (l+r)>>1 constexpr int MAXN=500005; struct node { int val=0,tag=0; //0代表tag為空 } seg[MAXN<<2]={}; inline void addtag(int cur,int x) { //處理seg[cur].val //處理seg[cur].tag } inline void push(int cur) { addtag(lc,seg[cur].tag), addtag(rc,seg[cur].tag); seg[cur].tag=0; // 標記設定為空 } void update(int cur,int l,int r,int ql,int qr,int x) { if (r<=ql || l>=qr) return; if(ql<=l && r<=qr) { addtag(cur,x); return; } push(cur); update(lc,l,mid,ql,qr,x); update(rc,mid,r,ql,qr,x); //pull } int query(int cur,int l,int r,int ql,int qr) { if (r<=ql || l>=qr) return 0; //回傳不可能的值 if (ql<=l && r<=qr) return seg[cur].val; push(cur); return //combine lc,rc; } #undef lc #undef rc #undef mid ``` ::: ### BIT "Binary index tree" or "Fenwick tree" 1. 單點加值 時間複雜度:修改、查詢$O(\log n)$ :::spoiler code ```C++= constexpr int MAXN=500005; int bit[MAXN+5]={}; inline int lsb(const int &x) { return x&-x; } inline void add(int i,int x) { while (i<MAXN) bit[i]+=x,i+=lsb(i); } inline int pre(int i) { int sum=0; while (i) sum+=bit[i],i-=lsb(i); return sum; } inline int sum(int l,int r) { return pre(r)-pre(l-1); } ``` ::: 2. 區間加值 $\sum\limits_{j=1}^i{a[j]}=(i+1)(\sum\limits_{j=1}^i{d[j]})-\sum\limits_{j=1}^i{j\times d[j]}$ 時間複雜度:修改、查詢$O(\log n)$ :::spoiler code ```C++ constexpr int MAXN=200005; int bit[2][MAXN+5]={}; //bit[0]->d, bit[1]->d*i inline int lsb(const int &x) { return x&-x; } void add(int l,int r,int x) //[l,r] { for (int i=l,xi=x*l;i<MAXN;i+=lsb(i)) bit[0][i]+=x,bit[1][i]+=xi; for (int i=r+1,xi=x*(r+1);i<MAXN;i+=lsb(i)) bit[0][i]-=x,bit[1][i]-=xi; } int pre(int i) //pre[i]=(i+1)*sum(d[j])-sum(d[j]*j), j:= 1->i { int x=0,mns=0; for (int j=i;j;j-=lsb(j)) x+=bit[0][j],mns+=bit[1][j]; return x*(i+1)-mns; } inline int sum(int l,int r) { return pre(r)-pre(l-1); } ``` ::: ### 莫隊 pending... ### ## 圖論 Graph theory ### 最小生成樹 MST 1. Kruskal's algorithm: 將邊以權重照順序拿取,若不成環則加入$MST$ $O(E\log E)$ :::spoiler code ```C++= //include DSU pair<int,pii> edge[MAXN]; vector<vector<vector<pii>>> mst; bool MST(int n,int m,int tr=0) { if (mst.size()<=tr) mst.resize(tr+1); mst[tr].assign(n+1,{}); DSU dsu(n); int cnt=0; sort(edge,edge+m); for (int i=0;i<m;i++) { int w=edge[i].fi,u=edge[i].se.fi,v=edge[i].se.se; if (!dsu.sme(u,v)) { mst[tr][u].EB(MP(w,v)); mst[tr][v].EB(MP(w,u)); dsu.join(u,v); cnt++; } } return cnt==n-1; } ``` ::: 2. Prim's algorithm: 先隨便選一個點,將點周圍跟尚未使用的點連接的邊加入(優先)佇列,依序挑出(優先)佇列的邊,若不成環則加入$MST$,然後更新(優先)佇列 $O(E\log V)$ :::spoiler code ```C++= vector<pii> edge[MAXN]; bitset<MAXN> used; priority_queue<pii,vector<pii>,greater<pii>> pq; bool MST(int n) { int cnt=0; used[1]=1; for (const pii& i : edge[1]) pq.emplace(i); while (!pq.empty()) { int v=pq.top().se; pq.pop(); if (used[v]) continue; used[v]=1; cnt++; for (const pii& i : edge[v]) if (!used[i.se]) pq.emplace(i); } return cnt+1==n; } ``` ::: ### 最短路徑問題 - 全圖最短路徑 1. Floyd-Warshall : $O(V^3)$ 對整張圖做$dp$,$dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])$ - 單點源最短路徑 1. Dijkstra : $O(E\log V)$ 支援無負邊圖,概念是greedy+BFS :::spoiler code ```C++ struct node { int v,w; node(int _v,int _w): v(_v), w(_w) {} bool operator < (const node& b) const { return w>b.w; } }; vector<node> edge[MAXN]; vi d(MAXN,-1); priority_queue<node> pq; int dij(int st,int ed,const auto& edge) { fill(ALL(d),-1); pq.emplace(node(st,0)); while (!pq.empty()) { auto tmp=pq.top(); pq.pop(); if (d[tmp.v]!=-1) continue; d[tmp.v]=tmp.w; for (auto &i : edge[tmp.v]) if (d[i.v]==-1) pq.emplace(node(i.v,i.w+d[tmp.v])); } return d[ed]; } ``` ::: 2. Bellmen-Ford : $O(VE)$ 支援有負邊圖,可以判斷負環,核心概念是relax,$dis(t)=min(dis(t),dis(s)+weight(s,t))$ :::spoiler code ```C++= #include<bits/stdc++.h> #define int long long #define itn int #define endl '\n' #define colinorz cin.tie(0);cout.tie(0);ios::sync_with_stdio(0); #define pii pair<int,int> #define fi first #define se second using namespace std; vector<vector<pii>> edge(100005); vector<int> d(100005,1e18); signed main() { int n,m,s,t; cin >> n >> m >> s >> t; for (int i=0;i<m;i++) { int u,v,w; cin >> u >> v >> w; edge[u].emplace_back(w,v); } bool nc=0; d[s]=0; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) for (auto &k:edge[j]) if (d[k.se]>k.fi+d[j]) { d[k.se]=k.fi+d[j]; if (i==n) nc=1; } if (nc) cout << "nc\n"; else cout << d[t] << endl; } ``` ::: 3. SPFA : $O(E)$~$O(VE)$ Bellman-Ford的優化,功能也一樣,期望複雜度很好,但是最差情況跟Bellmen-Ford一樣 :::spoiler code ```C++= struct node { int v,w; node (int _v,int _w): v(_v),w(_w) {} }; vector<vector<node>> edge(MAXN); vector<int> d(MAXN,INF),inq(MAXN,0),cnt(MAXN,0); queue<int> q; bool spfa(int st,int n,const auto& edge) { fill(ALL(d),INF); fill(ALL(inq),0); fill(ALL(cnt),0); q.emplace(st); d[st]=0; inq[st]=1; while (!q.empty()) { int u=q.front(); q.pop(); inq[u]=0; for (auto &i : edge[u]) { int w=i.w,v=i.v; if (d[v]>w+d[u]) { d[v]=w+d[u]; if (inq[v]) continue; q.emplace(v); inq[v]=1; if (++cnt[v]>n) return 0; } } } return 1; } ``` ::: ### LCA 1. Talgorithm : $O(V+E+q)$ **<font color="red">constraint: offline query</font>** :::spoiler code ```C++= vector<vi> edge(200005),qst(200005); bitset<200005> used; int boss[200005],qa[200005],qb[200005],lca[200005]; inline void dsu(int n) { for (int i=1;i<=n;i++) boss[i]=i; } //called in main int fnd(int a) { return a==boss[a]?a:boss[a]=fnd(boss[a]); } void dfs(int cur,int p) //watch out cur==v { for (const int& i : edge[cur]) if (i!=p) dfs(i,cur); used[cur]=1; for (const int& i : qst[cur]) { int v=cur^qa[i]^qb[i]; //the other vertex if (used[v]) lca[i]=fnd(v); } boss[cur]=p; } signed main() { //colinorz; int n,q; cin >> n >> q; dsu(n); for (int i=2;i<=n;i++) { int e; cin >> e; edge[e].EB(i); } for (int i=0;i<q;i++) { int a,b; cin >> a >> b; qst[a].EB(i); qst[b].EB(i); qa[i]=a; qb[i]=b; } dfs(1,-1); for (int i=0;i<q;i++) cout << lca[i] << endl; } ``` ::: 2. binary lifting + alignment(or timestamp) : $O(V\log V)$ :::spoiler code ```C++= constexpr int lg=16; vector<vi> edge(MAXN); int dpt[MAXN]={},jmp[MAXN][lg+5]; void dfs(int cur,int p,int dd) { dpt[cur]=dd; //build jmp[cur][0]=p; for (int stp=1;stp<=lg;stp++) jmp[cur][stp]=jmp[ jmp[cur][stp-1] ][ stp-1 ]; for (const int i : edge[cur]) if (i!=p) dfs(i,cur,dd+1); } int lca(int a,int b) { if (dpt[a]<dpt[b]) swap(a,b); //a jump to b's dpt int dif=dpt[a]-dpt[b]; for (int stp=lg;stp>=0;stp--) if (dif>=(1<<stp)) a=jmp[a][stp],dif-=1<<stp; if (a==b) return a; //fnd lca for (int stp=lg;stp>=0;stp--) if (jmp[a][stp]!=jmp[b][stp]) a=jmp[a][stp],b=jmp[b][stp]; return jmp[a][0]; } ``` ::: 3. Euler tour + Segment tree : $O(V\log V)$ :::spoiler code ```C++= int n,tp=0,dpt[30005]={(int)1e16},seg[(60000<<2)+5]={}; //[l,r) vi edge[30005],pos(30005,-1); #define lc cur<<1 #define rc cur<<1|1 #define mid (l+r)>>1 void update(int cur,int l,int r,int i,int x) //x -> pos { if (l+1==r) { seg[cur]=x; return; } if (i<mid) update(lc,l,mid,i,x); else update(rc,mid,r,i,x); seg[cur]=(dpt[seg[lc]]<dpt[seg[rc]]?seg[lc]:seg[rc]); } int query(int cur,int l,int r,int ql,int qr) { if (l>=qr || r<=ql) return 0; if (ql<=l && r<=qr) return seg[cur]; int lp=query(lc,l,mid,ql,qr),rp=query(rc,mid,r,ql,qr); return (dpt[lp]<dpt[rp]?lp:rp); } void dfs(int cur,int p,int dd) { dpt[cur]=dd; if (pos[cur]==-1) pos[cur]=tp; update(1,0,(n<<1)-1,tp++,cur); for (const int i : edge[cur]) if (i!=p) dfs(i,cur,dd+1),update(1,0,(n<<1)-1,tp++,cur); } inline int lca(int a,int b) { int ql=pos[a],qr=pos[b]; if (ql>qr) swap(ql,qr); return query(1,0,(n<<1)-1,ql,qr+1); } ``` ::: ### HLD - Heavy Light Decomposition(重鍊分解) - 例題:[CSES2134](https://cses.fi/problemset/task/2134) - 時間複雜度:預處理$O(V+E)$,查詢$O(q\cdot \log ^2 V)$ :::spoiler code ```C++= constexpr int MAXN=200005; vi edge[MAXN]; int n,tt=1,v[MAXN],sz[MAXN],dpt[MAXN],prt[MAXN],hc[MAXN]={},up[MAXN],pos[MAXN],seg[MAXN<<2]={}; void update(int cur,int l,int r,int i,int x) { int lc=cur<<1,rc=cur<<1|1,mid=(l+r)>>1; if (l+1==r) { seg[cur]=x; return; } if (i<mid) update(lc,l,mid,i,x); else update(rc,mid,r,i,x); seg[cur]=max(seg[lc],seg[rc]); } int query(int cur,int l,int r,int ql,int qr) { int lc=cur<<1,rc=cur<<1|1,mid=(l+r)>>1; if (l>=qr || r<=ql) return -1e9; if (ql<=l && r<=qr) return seg[cur]; return max(query(lc,l,mid,ql,qr),query(rc,mid,r,ql,qr)); } void dfs(int cur=1,int p=-1,int dd=1) { prt[cur]=p,dpt[cur]=dd; int sum=1,tmp=0,tmpsz=-1,csz; for (const int &i : edge[cur]) if (i!=p) { dfs(i,cur,dd+1); csz=sz[i],sum+=csz; if (csz>tmpsz) tmp=i,tmpsz=csz; } hc[cur]=tmp,sz[cur]=sum; } void hld(int cur=1,int tp=1) { up[cur]=tp,pos[cur]=tt++; update(1,1,n+1,pos[cur],v[cur]); if (hc[cur]) { hld(hc[cur],tp); for (const int &i : edge[cur]) if (i!=prt[cur] && i!=hc[cur]) hld(i,i); } } signed main() { //colinorz; int q; cin >> n >> q; for (int i=1;i<=n;i++) cin >> v[i]; for (int i=0;i+1<n;i++) { int a,b; cin >> a >> b; edge[a].EB(b); edge[b].EB(a); } dfs(1,-1,1); hld(1,1); while (q--) { int op,a,b; cin >> op >> a >> b; if (op==1) update(1,1,n+1,pos[a],b),v[a]=b; else { int ans=0; while (up[a]!=up[b]) { if (dpt[up[b]]>dpt[up[a]]) swap(a,b); //jmp a if (a==up[a]) ans=max(ans,v[a]); else ans=max(ans,query(1,1,n+1,pos[up[a]],pos[a]+1)); a=prt[up[a]]; } if (dpt[b]>dpt[a]) swap(a,b); ans=max(ans,query(1,1,n+1,pos[b],pos[a]+1)); cout << ans << ' '; } } } ``` ::: ### 強連通分量 SCC 1. Tarjan : $O(V+E)$ :::spoiler code ```C++= vi edge[MAXN]; int dfn[MAXN]={},up[MAXN],tp=0,scc_id[MAXN],scc_cnt=0; bool ins[MAXN]={}; stack<int> stk; void tar(int cur) { dfn[cur]=up[cur]=++tp; stk.emplace(cur); ins[cur]=1; for (int &i : edge[cur]) if (!dfn[i]) tar(i),up[cur]=min(up[cur],up[i]); else if (ins[i]) up[cur]=min(up[cur],dfn[i]); if (up[cur]==dfn[cur]) { int tmp; do { tmp=stk.top(); stk.pop(); scc_id[tmp]=scc_cnt; ins[tmp]=0; } while (tmp!=cur); scc_cnt++; } } inline void fnd_scc(int num) { for (int i=1;i<=num;i++) if (!dfn[i]) tar(i); } ``` ::: - 縮點 :::spoiler code ```C++= vi dag[MAXN]; int aa[MAXN]={}; void cnds(int n) //condense SCC { for (int i=1;i<=n;i++) { const int &cur=scc_id[i]; aa[cur]+=a[i]; for (int &j : edge[i]) if (cur!=scc_id[j]) dag[cur].EB(scc_id[j]); } } ``` ::: ### 歐拉迴路(一筆畫) 1. Hierholzer : $O(V+E)$ :::spoiler code ```C++ vi ans; void elc (int cur = 1) // euler circuit { while(!edge[cur].empty()) { pii i=edge[cur].back(); edge[cur].pop_back(); if (kll[i.se]) continue; kll [i.se]=1; elc (i.fi); } ans.EB(cur); } ``` ::: ### 網路流問題 - 最大流 Maximum Flow 1. Dinic : $O(V^2E)$ 通常會比理論上界快很多 :::spoiler code ```C++= struct node { int frm,to,cap; node(int f,int t,int c): frm(f),to(t),cap(c) {} }; vector<node> edge[MAXN]; int lvl[MAXN],ptr[MAXN],st,ed; queue<int> que; inline void d_edge(int u,int v,int c) //directed graph { node uu(edge[v].size(),v,c),vv(edge[u].size(),u,0); edge[u].EB(uu); edge[v].EB(vv); } inline void und_edge(int u,int v,int c) //undirected graph { node uu(edge[v].size(),v,c),vv(edge[u].size(),u,c); edge[u].EB(uu); edge[v].EB(vv); } bool bfs() { memset(lvl,-1,sizeof(lvl)); que.emplace(st); lvl[st]=1; while (!que.empty()) { auto tmp=que.front(); que.pop(); for (auto &i : edge[tmp]) { if (lvl[i.to]!=-1 || i.cap<=0) continue; que.emplace(i.to); lvl[i.to]=lvl[tmp]+1; } } return lvl[ed]!=-1; } int dfs(int cur=st,int fl=INF) { if (cur==ed) return fl; for (int &id=ptr[cur];id<edge[cur].size();id++) { auto &e=edge[cur][id]; if (lvl[e.to]!=lvl[cur]+1 || e.cap<=0) continue; int re=dfs(e.to,min(fl,e.cap)); if (!re) continue; e.cap-=re; edge[e.to][e.frm].cap+=re; return re; } return 0; } int dnc(int _st,int _ed) { st=_st; ed=_ed; int re=0; while (bfs()) { memset(ptr,0,sizeof(ptr)); while (int tmp=dfs()) re+=tmp; } return re; } ``` ::: ## 數論 Number theory ### 質數 - 質數判定 1. 試除法$O(\sqrt n)$ :::spoiler code ```C++= bool is_p(int n) { if (n<2) return 0; for (int i=2;i*i<=n;i++) if (n%i==0) return 0; return 1; } signed main() { //colinorz; int n; cin >> n; cout << is_p(n) << endl; } ``` ::: 2. Miller-Rabin $O(\log n)$ :::spoiler template ```C++= inline int mul(int a,int b,int m) //防溢位乘法 { unsigned int re=(unsigned int)a*b- (unsigned int)((long double)a/m*b+0.5L)*m; return re<m?re:re+m; } inline int fp(int a,int b,int m) { int re=1; a%=m; while (b) { if (b&1) re=re*a%m; b>>=1,a=a*a%m; } return re; } const int test[12]={2,3,5,7,11,13,17,19,23,29,31,37}; bool Miller_Rabin(int n) { if (n<3 || !(n&1)) return (n==2); for (const int i : test) if (i==n) return 1; int m=n-1,t=0; while (!(m&1)) m>>=1,t++; for (const int a : test) { int x=fp(a,m,n),isp=0; if (x==1 || x==n-1) continue; for (int i=0;!isp && i<t-1;i++) { x=mul(x,x,n); if (x==1) return 0; if (x==n-1) isp=1; } if (!isp) return 0; } return 1; } ``` ::: - 建質數表(線性篩法):$O(n)$ :::spoiler code ```C++= vector<int> p; int minp[1000005]={}; void build_p(int n) { for (int i=2;i<=n;i++) { if (!minp[i]) minp[i]=i,p.emplace_back(i); for (int j : p) { if (j>minp[i] || j*i>n) break; minp[j*i]=j; } } } signed main() { //colinorz; build_p(1000000); } ``` ::: ### 質因數分解 - 建質數表+試除法: $O(\sqrt n+q\ \sqrt{\pi (n)})$ pending... :::spoiler code ```C++= ``` ::: - Pollard's $\rho$: $O(\sqrt p)\approx O(n^{1/4})$ :::spoiler template ```C++= //define Miller_Rabin inline int gcd(int a,int b) { return (b?gcd(b,a%b):a); } int Pollard_rho(int n) { int x=rand()%n+1,y=x,c=rand()%(n-1)+1; int i=1,p=2; while (1) { x=(mul(x,x,n)+c)%n; int d=gcd(abs(x-y),n); if (d>1) return d; if (++i==p) y=x,p<<=1; } } vector<int> factors; void factorize(int n) { while (!(n&1)) n>>=1,factors.emplace_back(2); if (n==1) return; if (Miller_Rabin(n)) { factors.emplace_back(n); return; } int tmp; do tmp=Pollard_rho(n); while (tmp==n); factorize(tmp),factorize(n/tmp); } ``` ::: ### 擴展歐幾里得算法 - 求 $ax+by=c$ 的整數解: $\text{let}\ d=\gcd(a,b)$ $\begin{cases} \ x=x' \cdot \dfrac{c}{d},\; y=y' \cdot \dfrac{c}{d} & \text{, if } d \mid c \\ \text{no solution} & \text{, if } d \nmid c \end{cases}$ :::spoiler code ```C++= void exgcd(int a,int b,int &x,int &y,int &d) //solve ax+by=gcd(a,b) { if (b) exgcd(b,a%b,y,x,d),y-=a/b*x; else d=a,x=1,y=0; } inline pii sol(int a,int b,int c) //solve ax+by=c { int x,y,d; exgcd(a,b,x,y,d); if (c%d) return pii(1e18,1e18); //no solution return pii(x/d*c,y/d*c); } ``` ::: ### 模逆元 1. 當 MOD **是**質數時可使用快速冪 $a^{-1}\equiv a^{p-2} \pmod p$ :::spoiler code ```C++= inline int fp(int a,int b,int m) { int ans=1; a%=m; while (b) { if (b&1) ans=ans*a%m; b>>=1,a=a*a%m; } return ans; } inline int inv(int a) { return fp(a,MOD-2,MOD); } ``` ::: 2. 當 MOD **不是**質數時使用exgcd :::spoiler code ```C++= void exgcd(int a,int b,int &x,int &y,int &d) //solve ax+by=gcd(a,b) { if (b) exgcd(b,a%b,y,x,d),y-=a/b*x; else d=a,x=1,y=0; } inline int inv(int a,int m) { int x,y,d; exgcd(a,m,x,y,d); if (d==1) return (x%m+m)%m; return -1; //non-existent } ``` ::: ### CRT :::spoiler code ```C++ int crt(const vi& a,const vi& r) { int re=0,sz=a.size(),m=1; for (int i=0;i<sz;i++) m*=r[i]; for (int i=0;i<sz;i++) { int mi=m/r[i],k=inv(mi,r[i])%m; int c=((__int128)mi*k)%m,tot=((__int128)a[i]*c)%m; re=(re+tot)%m; } return (re%m+m)%m; } ``` ::: ## String ### String Match 1. rolling-hash :::spoiler code ```C++ #include<bits/stdc++.h> #define int long long #define itn int #define colinorz cin.tie(0);cout.tie(0);ios::sync_with_stdio(0); using namespace std; constexpr int MOD=1e9+7,P=203; string s,t; signed main() { //colinorz; cin >> s >> t; if (s.size()<t.size()) { cout << "0\n"; return 0; } int tar=0; for (int i=0,pp=1;i<t.size();i++,pp=(pp*P)%MOD) tar=(tar+t[i]*pp)%MOD; int k=0,ans=0; for (int i=s.size()-t.size(),pp=1;i<s.size();i++,pp=(pp*P)%MOD) k=(k+s[i]*pp)%MOD; if (k==tar) ans++; int mp=1; for (int i=0;i<t.size();i++) mp=(mp*P)%MOD; for (int j=(int)s.size()-t.size()-1;j>=0;j--) { k=((k*P+s[j]-(s[j+t.size()]*mp)%MOD)%MOD+MOD)%MOD; if (k==tar) ans++; } cout << ans << endl; } ``` :::