noip模板整理
2016-11-11 17:21
295 查看
距离noip还有⑨-1天,差不多要开始撸模板了,在这里整理下noip各式各样算法的模板。
Kruskal,最小生成树,好像可以跟01分数规划搞一搞?
拓扑排序,图上dp有时会用到╮(╯▽╰)╭。
树上最近公共祖先,lca,这里推荐倍增版,滋磁在树上快速搞事。
tarjan系列
tarjan 求 scc , 有向图大腿。
tarjan求割点,然而基本没用过,忘得差不多了。
tarjan求桥,就是割点改个等于号。
树的直径,滋磁树上乱搞,这里以codevs1814为例。
floyd,n^3最短路,带有比较特殊的性质,可以有各种变形,但往往难度超出noip范围。
埃氏筛,筛素数速度上仅次于欧拉筛。
快速幂,快速求一个数的次方,搞事必备
逆元,只推荐费马小定理,要是noip题mod不是素数我就暴力!
费马小定理: 假如p是质数,且a,p互质,那么 a^(p-1)≡1(mod p)。
由此可得,a^(p-2) ≡ 1 / a (mod p),所以在mod p意义下,除以 a 等价于乘上 a^(p-2),即 a^(p-2) 为 a 的逆元。
set,用于logn找前驱后继或当map使233,这里以noi openjudge的冷血格斗场为例。
线段树,noip考不到但是可以水分的大腿,滋磁区间快速搞事。
归并排序求逆序对是唯有的几个比较高效的求逆序对算法(线段树:???),前几年noip有用到。
状压搜索,noip还真没见过,模拟赛里倒是不少。
以HAOI2008移动玩具为例。
论如何正确地打开脑洞
自己的码力实现不了的做法还是少想;
看数据范围估计复杂度,看有没有套路,有没有可以套的模型,看特殊的条件等等。
图论 :
(论图╮(╯▽╰)╭)
spfa:图上乱搞必备,并非只止步于求最短路 | 最长路,spfa可是图上dp!!#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int sz = 200100; deque < int > q; // slf int n,m; int head[sz],nxt[sz],dist[sz]; struct gtnd { int t,d; }l[sz]; int tot = 1; bool use[sz]; int pre[sz];//记录路径 int tim[sz];//判负环 void build(int f,int t,int d) { l[tot].t = t; l[tot].d = d; nxt[tot] = head[f]; head[f] = tot ++; } int spfa(int s,int e) { for(int i = 1 ; i <= n ; i ++) dist[i] = 2147483641; dist[s] = 0; use[s] = 1; q.push_front(s); while(!q.empty()) { int f = q.front(); q.pop_front(); use[f] = 0; for(int i = head[f] ; i ; i = nxt[i]) { int t = l[i].t; if(dist[t] > dist[f] + l[i].d) { dist[t] = dist[f] + l[i].d; tim[t] = tim[f] + 1; if(tim[t] > n) return -1; pre[t] = f; if(!use[t]) { use[t] = 1; if(!q.empty()) { if(dist[t] < dist[q.front()]) q.push_front(t); else q.push_back(t); } else q.push_back(t); } } } } return dist[e]; } void print_path(int u) // 打印路径 { printf("%d ",u); if(pre[u]) print_path(pre[u]); } int main() { return 0; }
Kruskal,最小生成树,好像可以跟01分数规划搞一搞?
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int sz = 200010; int f[sz]; int n,m; int find(int x) { if(f[x] == x) return x; return f[x] = find(f[x]); } struct gtnd { int f,t,d; }l[sz]; bool cmp(gtnd swc,gtnd zcw) { return swc.d > zcw.d; } int Kruskal() { int ans = 0; for(int i = 1 ; i <= n ; i ++) f[i] = i; sort(l+1,l+m+1,cmp); for(int i = 1 ; i <= m ; i ++) { int u = l[i].f , v = l[i].t; int fu = find(u) , fv = find(v); if(fu != fv) { f[fu] = fv; ans += l[i].d; } } return ans; } int main() { return 0; }
拓扑排序,图上dp有时会用到╮(╯▽╰)╭。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int sz = 200010; int head[sz],nxt[sz],l[sz]; int ru[sz]; int tot = 1; int n,m; queue < int > q; void build(int f,int t) { l[tot] = t; nxt[tot] = head[f]; head[f] = tot ++; } void top_sort() { for(int i = 1 ; i <= n ; i ++) if(!ru[i]) { q.push(i); printf("%d ",i); } while(!q.empty()) { int f = q.front(); q.pop(); for(int i = head[f] ; i ; i = nxt[i]) { ru[l[i]] --; if(!ru[l[i]]) { printf("%d ",l[i]); q.push(l[i]); } } } } int main() { scanf("%d%d",&n,&m); for(int i = 1 ; i <= m ; i ++) { int f,t; scanf("%d%d",&f,&t); ru[t] ++; build(f,t); } top_sort(); return 0; }
树上最近公共祖先,lca,这里推荐倍增版,滋磁在树上快速搞事。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int size = 200010; int head[size],next[size],dist[size][32]; int par[size][32],deep[size]; int tot = 1; struct dc { int t,d; }l[size]; void build(int f,int t,int d) { l[tot].t = t; l[tot].d = d; next[tot] = head[f]; head[f] = tot ++; } int n; void dfs(int u,int fa,int dep,int dis) { par[u][0] = fa; dist[u][0] = dis; deep[u] = dep; for(int i = head[u] ; i ; i = next[i]) { int v = l[i].t; if(v != fa) dfs(v,u,dep+1,l[i].d); } } int lca(int u,int v) { int ans = 0; if(deep[u] < deep[v]) swap(u,v); for(int i = 31 ; i >= 0 ; i --) if(deep[par[u][i]] >= deep[v]) ans += dist[u][i] , u = par[u][i]; for(int i = 31 ; i >= 0 ; i --) if(par[u][i] != par[v][i]) ans += dist[u][i] + dist[v][i] , u = par[u][i] , v = par[v][i]; if(u != v) ans += dist[u][0] + dist[v][0] , u = par[u][0] , v = par[v][0]; return ans; } int main() { scanf("%d",&n); for(int i = 1 ; i < n ; i ++) { int f,t,d; scanf("%d%d%d",&f,&t,&d); f ++ , t ++; build(f,t,d); build(t,f,d); } dfs(1,0,1,0); for(int i = 1 ; i <= 31 ; i ++) for(int j = 1 ; j <= n ; j ++) par[j][i] = par[par[j][i-1]][i-1] , dist[j][i] = dist[j][i-1] + dist[par[j][i-1]][i-1]; int m; scanf("%d",&m); for(int i = 1 ; i <= m ; i ++) { int u,v; scanf("%d%d",&u,&v); printf("%d\n",lca(u+1,v+1)); } return 0; }
tarjan系列
tarjan 求 scc , 有向图大腿。
#include<iostream> #include<cstdio> #include<cstring> #include<stack> #include<algorithm> using namespace std; const int sz = 200010; int head[sz],nxt[sz],dist[sz],l[sz]; int low[sz],dfn[sz],scc_num,dfs_clock; stack < int > s; struct gtnd { int p,num; bool operator <(const gtnd &a)const { return num < a.num; } }scc[sz]; int tarjan(int u) { dfn[u] = low[u] = dfs_clock ++; s.push(u); for(int i = head[u] ; i ; i = nxt[i]) { int v = l[i]; if(!dfn[v]) { low[v] = tarjan(v); low[u] = min(low[u],low[v]); } else if(!scc[v].num) low[u] = min(low[u],dfn[v]); } if(low[u] == dfn[u]) { scc_num ++; while(12 < 450) { if(s.empty()) break; int v = s.top(); s.pop(); scc[v].num = scc_num; scc[v].p = v; if(u == v) break; } } return low[u]; } int main() { return 0; }
tarjan求割点,然而基本没用过,忘得差不多了。
#include<iostream> #include<cstdio> #include<cstring> #include<stack> #include<algorithm> using namespace std; const int sz = 200010; int head[sz],nxt[sz],l[sz]; int tot = 1; int n,m; void build(int f,int t) { l[tot] = t; nxt[tot] = head[f]; head[f] = tot ++; } int low[sz],dfn[sz],dfs_clock; bool is_cut[sz]; int tarjan(int u,int fa) { dfn[u] = low[u] = ++ dfs_clock; int child = 0; for(int i = head[u] ; i ; i = nxt[i]) { int v = l[i]; if(!dfn[v]) { child ++; low[v] = tarjan(v,u); low[u] = min(low[u],low[v]); if(dfn[u] <= low[v]) is_cut[u] = 1; } else if(dfn[v] < dfn[u] && v != fa) low[u] = min(dfn[v],low[u]); } if(child == 1 && fa == 0) is_cut[u] = 0; return low[u]; } int main() { scanf("%d%d",&n,&m); for(int i = 1 ; i <= m ; i ++) { int f,t; scanf("%d%d",&f,&t); build(f,t); build(t,f); } for(int i = 1 ; i <= n ; i ++) if(!dfn[i]) tarjan(i,0); int ans = 0; for(int i = 1 ; i <= n ; i ++) if(is_cut[i]) ans ++; printf("%d\n",ans); for(int i = 1 ; i <= n ; i ++) if(is_cut[i]) printf("%d ",i); return 0; }
tarjan求桥,就是割点改个等于号。
int low[sz],dfn[sz],dfs_clock; struct gtnd { int f,t; }cut[sz]; int conut; int tarjan(int u,int fa) { dfn[u] = low[u] = ++ dfs_clock; int child = 0; for(int i = head[u] ; i ; i = nxt[i]) { int v = l[i]; if(!dfn[v]) { child ++; low[v] = tarjan(v,u); low[u] = min(low[u],low[v]); if(dfn[u] < low[v]) cut[++conut].f = u , cut[count].t = v; } else if(dfn[v] < dfn[u] && v != fa) low[u] = min(dfn[v],low[u]); } return low[u]; }
树的直径,滋磁树上乱搞,这里以codevs1814为例。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n; const int size = 200010; int head[size],next[size],l[size]; int tot = 1; void build(int f,int t) { l[tot] = t; next[tot] = head[f]; head[f] = tot ++; } int pos,dist; void dfs(int u,int p,int dis) { if(dis > dist) dist = dis , pos = u; for(int i = head[u] ; i ; i = next[i]) { int v = l[i]; if(v != p) dfs(v,u,dis+1); } } int main() { scanf("%d",&n); for(int i = 1 ; i <= n ; i ++) { int ll,rr; scanf("%d%d",&ll,&rr); if(ll) build(ll,i) , build(i,ll); if(rr) build(rr,i) , build(i,rr); } dfs(1,-1,0); dist = 0; dfs(pos,-1,0); printf("%d\n",dist); return 0; }
floyd,n^3最短路,带有比较特殊的性质,可以有各种变形,但往往难度超出noip范围。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int sz = 2550; int dis[sz][sz]; int n,m,s,e; int main() { scanf("%d%d%d%d",&n,&m,&s,&e); for(int i = 1 ; i <= n ; i ++) for(int j = 1 ; j <= n ; j ++) dis[i][j] = 214748364; for(int i = 1 ; i <= n ; i ++) dis[i][i] = 0; for(int i = 1 ; i <= m ; i ++) { int f,t,d; scanf("%d%d%d",&f,&t,&d); dis[f][t] = min(dis[f][t],d); dis[t][f] = min(dis[f][t],dis[t][f]); } for(int k = 1 ; k <= n ; k ++) for(int i = 1 ; i <= n ; i ++) for(int j = 1 ; j <= n ; j ++) dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]); printf("%d\n",dis[s][e]); return 0; }
数论
暴力(划);
gcd & lcm ,noip唯有的几个可以加特技的算法。#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int gcd(int a,int b) { if(b == 0) return a; return gcd(b,a%b); } int lcm(int a,int b) { return a * b / gcd(a,b); } int main() { int a,b; while(scanf("%d%d",&a,&b)) { printf("%d\n",gcd(a,b)); } return 0; }
埃氏筛,筛素数速度上仅次于欧拉筛。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; bool is_prime[2000100]; int main() { int n; scanf("%d",&n); is_prime[1] = 1; for(int i = 2 ; i <= n ; i ++) { if(!is_prime[i]) for(int j = i * i ; j <= n ; j += i) is_prime[j] = 1; } return 0; }
快速幂,快速求一个数的次方,搞事必备
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int ksm(int x,int p) { if(p == 0) return 1; if(p == 1) return x; if(p == 2) return x * x; int temp = ksm(x,p/2); if(p % 2 == 1) return temp * temp * x; if(p % 2 == 0) return temp * temp; } int main() { int x,p; while(scanf("%d%d",&x,&p)) printf("%d\n",ksm(x,p)); return 0; }
逆元,只推荐费马小定理,要是noip题mod不是素数我就暴力!
费马小定理: 假如p是质数,且a,p互质,那么 a^(p-1)≡1(mod p)。
由此可得,a^(p-2) ≡ 1 / a (mod p),所以在mod p意义下,除以 a 等价于乘上 a^(p-2),即 a^(p-2) 为 a 的逆元。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int mod = 1000000007; ll ksm(ll x,ll p) { if(p == 0) return 1; if(p == 1) return x % mod; if(p == 2) return ((x%mod) * (x%mod))%mod; int temp = ksm(x,p/2) % mod; if(p % 2 == 1) return (((temp * temp) % mod) * (x%mod)); if(p % 2 == 0) return (temp * temp) % mod; } int get(int a) { return ksm(a,mod-2); } int main() { return 0; }
数据结构
单调队列,滋磁O(n)序列上搞事,多用于dp优化。#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int sz = 2000100; deque < int > q; int num[sz]; int n,k; int main() { scanf("%d%d",&n,&k); for(int i = 1 ; i <= n ; i ++) scanf("%d",&num[i]); for(int i = 1 ; i <= k ; i ++) { while(!q.empty() && num[q.back()] < num[i]) q.pop_back(); q.push_back(i); } printf("%d\n",num[q.front()]); for(int i = k + 1 ; i <= n ; i ++) { while(!q.empty() && q.front() < i - k + 1) q.pop_front(); while(!q.empty() && num[q.back()] < num[i]) q.pop_back(); q.push_back(i); printf("%d\n",num[q.front()]); } return 0; }
set,用于logn找前驱后继或当map使233,这里以noi openjudge的冷血格斗场为例。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<set> #include<map> #define ll long long using namespace std; struct gtnd { ll id; ll p; bool operator <(const gtnd &a)const { return p < a.p; } }; map < ll , ll > m; set < gtnd > s; set < gtnd > :: iterator f1,f2,temp; int n; int main() { scanf("%d",&n); gtnd sta; sta.id = 1 , sta.p = 1000000000; m[sta.p] = 1; s.insert(sta); for(int i = 1 ; i <= n ; i ++) { gtnd nxt; scanf("%lld%lld",&nxt.id,&nxt.p); printf("%lld ",nxt.id); if(m[nxt.p]) { printf("%lld\n",m[nxt.p]); m[nxt.p] = min(m[nxt.p],nxt.id); continue; } else m[nxt.p] = nxt.id; f1 = s.lower_bound(nxt); f2 = f1; f2 --; ll id_f1 = m[(*f1).p] , id_f2 = m[(*f2).p]; ll a_f1 = abs((*f1).p - nxt.p) , a_f2 = abs((*f2).p - nxt.p); if(f2 == s.end()) printf("%lld\n",id_f1); else if(f1 == s.end()) printf("%lld\n",id_f2); else if(a_f1 > a_f2) printf("%lld\n",id_f2); else if(a_f1 < a_f2) printf("%lld\n",id_f1); else { if(id_f1 < id_f2) printf("%lld\n",id_f1); else printf("%lld\n",id_f2); } s.insert(nxt); } return 0; }
线段树,noip考不到但是可以水分的大腿,滋磁区间快速搞事。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int sz = 200010; struct xd_tree { int l,r; ll sum,min,max,add; }tree[sz*4]; int num[sz]; int n; void updata(int p) { tree[p].min = min(tree[p<<1].min,tree[p<<1|1].min); tree[p].max = max(tree[p<<1].max,tree[p<<1|1].max); tree[p].sum = tree[p<<1].sum + tree[p<<1|1].sum; } void build_tree(int p,int l,int r) { tree[p].l = l , tree[p].r = r; if(l == r) { tree[p].min = tree[p].max = tree[p].sum = num[l]; return ; } int mid = l + r >> 1; build_tree(p<<1,l,mid); build_tree(p<<1|1,mid+1,r); updata(p); } void spread(int p) { if(tree[p].add) { tree[p<<1].sum += tree[p].add * (tree[p<<1].r - tree[p<<1].l + 1); tree[p<<1|1].sum += tree[p].add * (tree[p<<1|1].r - tree[p<<1|1].l + 1); tree[p<<1].min += tree[p].add; tree[p<<1|1].min += tree[p].add; tree[p<<1].max += tree[p].add; tree[p<<1|1].max += tree[p].add; tree[p<<1].add += tree[p].add; tree[p<<1|1].add += tree[p].add; tree[p].add = 0; } return ; } void change(int p,int l,int r,int x) { if(l <= tree[p].l && tree[p].r <= r) { tree[p].sum += x * (tree[p].r - tree[p].l + 1); tree[p].max += x; tree[p].min += x; tree[p].add += x; return ; } spread(p); int mid = tree[p].l + tree[p].r >> 1; if(l <= mid) change(p<<1,l,r,x); if(r > mid) change(p<<1|1,l,r,x); updata(p); } ll ask_sum(int p,int l,int r) { if(l <= tree[p].l && tree[p].r <= r) return tree[p].sum; spread(p); int mid = tree[p].l + tree[p].r >> 1; ll ans = 0; if(l <= mid) ans += ask_sum(p<<1,l,r); if(r > mid) ans += ask_sum(p<<1|1,l,r); return ans; } ll ask_min(int p,int l,int r) { if(l <= tree[p].l && tree[p].r <= r) return tree[p].min; spread(p); int mid = tree[p].l + tree[p].r >> 1; ll ans = 214748364111111ll; if(l <= mid) ans = min(ask_min(p<<1,l,r),ans); if(r > mid) ans = min(ask_min(p<<1|1,l,r),ans); return ans; } ll ask_max(int p,int l,int r) { if(l <= tree[p].l && tree[p].r <= r) return tree[p].max; spread(p); int mid = tree[p].l + tree[p].r >> 1; ll ans = 0; if(l <= mid) ans = max(ask_max(p<<1,l,r),ans); if(r > mid) ans = max(ask_max(p<<1|1,l,r),ans); return ans; } int main() { scanf("%d",&n); for(int i = 1 ; i <= n ; i ++) scanf("%d",&num[i]); build_tree(1,1,n); return 0; }
其它的一些奇怪的算法 | 姿势
逆序对 && 归并排序归并排序求逆序对是唯有的几个比较高效的求逆序对算法(线段树:???),前几年noip有用到。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int sz = 2000100; ll ans; int n; int num[sz]; int temp[sz]; void merge_sort(int l,int r) { if(l == r) return ; int mid = l + r >> 1; merge_sort(l,mid) , merge_sort(mid+1,r); int p = l , pl = l , pr = mid + 1; while(pl <= mid || pr <= r) { if(pr > r || (pl <= mid && num[pl] <= num[pr])) temp[p ++] = num[pl ++]; else temp[p ++] = num[pr ++] , ans += mid - pl + 1; } for(int i = l ; i <= r ; i ++) num[i] = temp[i]; return ; } int main() { int n; scanf("%d",&n); for(int i = 1 ; i <= n ; i ++) scanf("%d",&num[i]); merge_sort(1,n); printf("%lld\n",ans); return 0; }
状压搜索,noip还真没见过,模拟赛里倒是不少。
以HAOI2008移动玩具为例。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; bool use[2000100]; struct gtnd { int k; int now; }; queue < gtnd > q; int st,ed; void start_work() { for(int i = 0 ; i < 16 ; i ++) { char ins = getchar(); while(ins != '1' && ins != '0') ins = getchar(); ins -= '0'; st |= ( ins << i ); } for(int i = 0 ; i < 16 ; i ++) { char ins = getchar(); while(ins != '1' && ins != '0') ins = getchar(); ins -= '0'; ed |= ( ins << i ); } } int bfs() { gtnd star; star.k = st; star.now = 0; q.push(star); use[st] = 1; while(!q.empty()) { gtnd f = q.front(); q.pop(); if(f.k == ed) return f.now; for(int i = 0 ; i < 16 ; i ++) { if((f.k >> i) & 1) { if(i > 3 && !((f.k >> (i-4)) & 1)) { gtnd nxt; nxt.now = f.now + 1; nxt.k = f.k; nxt.k ^= (1 << i-4); nxt.k ^= (1 << i); if(!use[nxt.k]) { use[nxt.k] = 1; q.push(nxt); } } if(i < 12 && !((f.k >> (i+4)) & 1)) { gtnd nxt; nxt.now = f.now + 1; nxt.k = f.k; nxt.k ^= (1 << i+4); nxt.k ^= (1 << i); if(!use[nxt.k]) { use[nxt.k] = 1; q.push(nxt); } } if(i % 4 != 0 && !((f.k >> (i-1)) & 1)) { gtnd nxt; nxt.now = f.now + 1; nxt.k = f.k; nxt.k ^= (1 << i-1); nxt.k ^= (1 << i); if(!use[nxt.k]) { use[nxt.k] = 1; q.push(nxt); } } if(i % 4 != 3 && !((f.k >> (i+1)) & 1)) { gtnd nxt; nxt.now = f.now + 1; nxt.k = f.k; nxt.k ^= (1 << i+1); nxt.k ^= (1 << i); if(!use[nxt.k]) { use[nxt.k] = 1; q.push(nxt); } } } } } return -1; } int main() { start_work(); printf("%d\n",bfs()); return 0; }
论如何正确地打开脑洞
自己的码力实现不了的做法还是少想;
看数据范围估计复杂度,看有没有套路,有没有可以套的模型,看特殊的条件等等。
相关文章推荐
- NOIP模板整理计划
- NOIP知识汇总及模板整理
- NOIP 模板整理
- NOIP 模板整理(多图预警╮(╯▽╰)╭)
- [置顶] NOIP模板整理
- [置顶] NOIP 模板整理计划 NOIP2017 RP++(持续更新中~)
- NOIP 前夕 模板整理
- [置顶] NOIP前夕模板整理第一弹:图论
- NOIP 前夕 模板整理
- NOIP前模板整理
- NOIP 考前模板整理
- [置顶] NOIP前夕模板整理第二弹:数据结构
- 16年NOIP复赛前各种模板的整理
- noip复赛前的模板整合
- ADO.NET数据访问模板整理
- 算法学习之KMP(模板整理)
- 背包整理模板
- NOIP 信息学 奥赛 考纲 考点 模板 裸题 水题
- KMP——模板整理
- 模板整理:数论---组合数/欧几里得/孙子定理/费马小定理/欧拉定理及相关