noip模板整理
来源:互联网 发布:apache phoenix 编辑:程序博客网 时间:2024/05/22 11:54
距离noip还有⑨-1天,差不多要开始撸模板了,在这里整理下noip各式各样算法的模板。
图论 :
(论图╮(╯▽╰)╭)
spfa:图上乱搞必备,并非只止步于求最短路 | 最长路,spfa可是图上dp!!
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int sz = 200100;deque < int > q; // slfint 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 longusing 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 * 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 longusing 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 longusing 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 longusing 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 longusing 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前夕模板整理第一弹:图论
- NOIP前夕模板整理第二弹:数据结构
- NOIP知识汇总及模板整理
- 16年NOIP复赛前各种模板的整理
- NOIP 模板整理计划 NOIP2017 RP++(持续更新中~)
- NOIP模板
- NOIP 模板整理(多图预警╮(╯▽╰)╭)
- NOIP历年搜索整理
- NOIP模板大全
- 【NOIP模板】 tarjan
- 【NOIP模板】KMP
- 【NOIP模板】 树状数组
- 【NOIP模板】 线段树
- 批量获取内码
- 单个字符比较问题
- 修改 framework 代码的经验和踩过的坑
- src refspec xxx does not match any 错误处理办法
- Python之post接口
- noip模板整理
- P2615 神奇的幻方题解NOIP2015day1
- Python Web Development with Flask
- java异常详解
- scala的 apply方法和update方法
- Java核心卷学习笔记——接口
- 承认失败痛苦,单为了理想奋斗绝对值得
- MongoDb相关
- 转:缓存架构设计细节二三事