冲刺NOIp2016算法模板
来源:互联网 发布:瑜伽初学者软件 编辑:程序博客网 时间:2024/05/21 09:30
- 数据结构
- 栈
- 队列
- 树状数组
- 应用模型树状数组求逆序对
- STL
- 不定长度数组
- Queue
- Deque
- Bitset
- Set
- Multiset
- Map
- Algorithm里其他好用的函数
- Next_permutation
- Lower_bound与Upper_bound
- Merge
- sort起始地址结束地址compare函数
- Reverse
- Unique
- 数论
- 快速幂
- 矩阵快速幂
- 筛法求素数 欧拉筛法
- 最大公约数和最小公倍数
- 扩展欧几里德
- 欧拉函数PHI
- 求n的欧拉函数值
- 逆元
- 扩展欧几里德求解
- 递推
- Catalan数
- 高精
- 高精度加法
- 高精加单精
- 高精加高精
- 高精度乘法
- 高精乘单精
- 高精乘高精
- 高精度除法
- 压位
- 高精度加法
- 图论
- 最短路
- 单源最短路SPFA
- 多源最短路FLOYD
- 次短路
- 最小生成树MST
- Kruskal
- 普通方法类似Prim算法
- 使用并查集优化
- TARJIAN
- 图的遍历
- 建树
- LCA
- 线段树
- 树状数组
- 单点修改区间查询codevs1080 线段树练习
- 区间修改单点查询codevs1081 线段树练习 2
- 区间修改区间查询codevs1082 线段树练习 3
- 并查集
- 最短路
- 字符串
- KMP
- TRIE树
- MANACHER
- 动态规划
- 01背包
- 完全背包
- 分组背包多维背包多重限制背包迷
- 其他模板
- 归并排序逆序对
- 二分
- 网络流dinic
- 最长上升子序列
- 最长公共子序列
- RMQ区间最值
- TREAP
- 匈牙利算法
- 数据结构
数据结构
栈
int strack[maxn];int head;bool b[maxn];void push(int x){ strack[++head]=x; b[x]=true;};int pop(){ int ret; ret=strack[head--]; b[ret]=false; return ret;};bool empty(){ return head>0;}
队列
int queue[2*maxn];int tail,head;bool b[maxn];void push(int x){ queue[++tail]=x; bool[x]=true;};int pop(){ int ret; ret=queue[++head]; b[ret]=false; return ret;};bool empty(){ return head>=tail;};
当然有的时候你手写的数据结构需要比较大的空间,这样队列就会造成很多损失,所以相应的就有两种解决方法:
一:STL;
二:循环队列,只需改两个地方(代码如下);
head=(head+1)%n+1;//把head++改tail=(tail+1)%n+1;//把tail++改
树状数组
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int n,m;int sum[500005];inline int lowbit (int x){ return x&-x;}int add (int k,int a){ while (k<=n) { sum[k]+=a; k+=lowbit(k); }}int out (int k){ int ans=0; while(k) { ans+=sum[k]; k-=lowbit(k); } return ans;}int main(){ scanf("%d%d",&n,&m); int a,b; for(int i=1;i<=n;i++) { scanf("%d",&a); add(i,a); } for(int i=1;i<=m;i++) { scanf("%d",&a); if (a==1) { scanf("%d%d",&a,&b); add (a,b); } else { scanf("%d%d",&a,&b); printf("%d\n",out(b)-out(a-1)); } }return 0;}
应用模型:树状数组求逆序对
void update(int n){ while(n<=maxn) { c[n]+=1; n+=lowbit(n); };};main():for(int i = 1; i <= n; ++i) //主程序里面加上这个{ update(reflect[i]); ans += i - getsum(reflect[i]);//reflect是离散化后的数组}
STL
关于每个STL我只会写一下是什么,怎么用(举例子的形式),不会说的太细
Vector
不定长度数组
#include <vector>vector<int> first; //第一种定义方法int myints[]={16,2,77,29};vector<int> second(myints,myints+4);//第二种定义方法sort(second.begin(),second.end());//对vector排序a=second[i];//可以这么使用//以下是对vector的操作Vector<int> opt;opt.begin(); //返回起始地址opt.end(); //返回结束地址opt.size(); //返回大小opt.empty(); //返回是否vector为空opt.back(); //返回最后一个push进的数opt.pop_back(); //把最后一个数弹出(不返回)opt.push_back(int x);//把x从后面push进去opt.erase(opt.begin(),opt.begin()+3);//删掉前三个元素opt.erase(opt.begin()+5);//删掉第6个元素
Queue
队列,操作与Stack一样。
Priority_queue
相当于堆
#include <queue>priority_queue<int> Bigheap;//定义一个大根堆priority_queue<int,vector<int>,greater<int> > Smallheap;//定义一个小根对(注意两个尖括号间有空格)//以下是操作priority_queue<int> opt;opt.top();//返回堆顶元素的值(不弹出)opt.pop();//弹出堆顶元素(无返回值)opt.push(x);Stackstack<int> opt;opt.front();//返回opt.size();opt.empty();opt.push();opt.pop();//弹出
Deque
双向队,操作与Stack一样
Bitset
压位神器,只普及一下,不会用。
Set
set<int> first;int myints[]= {10,20,30,40,50};set<int> second (myints,myints+5);set<int> third (second);set<int> fourth (second.begin(), second.end());third.rbegin(); third.rend();//rend相当于begin,rbegin相当于endthird.size();//返回大小third.insert(60);third.erase(it);third.erase(50);//删除元素'50'third.find(10);//找元素'10'third.lower_bound(30); third.upper_bound(30);//'30'出现的第一个位置/最后一个位置third.clear();//清除
Multiset
与Set用法一样,只是允许重复元素。
Map
map<char,int> first;first[‘a’] = 10;first.insert(make_pair(‘b’,20));it++; ++it; it--; --it;first.erase(1);//删除元素firstt.count(1);//看有没有关系
Algorithm里其他好用的函数
Next_permutation
int a[]={1,2,3,4};next_permutation(a,a+3);//下一个全排列//现在a数组变成了:1 2 4 3
Lower_bound与Upper_bound
a=lower_bound(first,last,val)-a;//有返回值upper_bound(first,last,val);
Merge
merge (first,first+5,second,second+5,v.begin(),compare);sortbool compare(int a,int b){ return a<b;};//compare函数的例子
sort(起始地址,结束地址,compare函数)
;
Reverse
Reverse(myvector.begin(),myvector.end());
Unique
bool myfunction (int i, int j){ return (i==j);}unique(起始地址,结束地址,去重条件函数);//按照函数里面编写的规则去重,当然也可以没有第三个参数
数论
快速幂
计算
long long fast(long long base,long long n,long long p){ long long s=1; while(n) { if(n&1) s=(s*base)%p; base=(base*base)%p; n=n>>1; } return s;}
矩阵快速幂
#include <iostream>#include <cmath>#include <cstring>#include <algorithm>#include <cstdio>#define mod=1e9+7;using namespace std;long long n,k;struct sq{ long long a[110][110]; sq(){memset(a,0,sizeof(a));}};void pre(sq &x){ for (long long i=1;i<=n;i++) x.a[i][i]=1;}sq operator * (const sq &x,const sq &y){ sq c; for (long long i=1;i<=n;i++) for (long long j=1;j<=n;j++) for (long long p=1;p<=n;p++) c.a[i][j]=(c.a[i][j]+x.a[i][p]*y.a[p][j])%mod; return c;}int main(){ cin>>n>>k; sq base; for (long long i=1;i<=n;i++){ for (long long j=1;j<=n;j++){ scanf("%lld",&base.a[i][j]); } } sq ans; pre(ans); while (k) { if (k&1) ans=ans*base; k>>=1; base=base*base; } for (long long i=1;i<=n;i++) { for (long long j=1;j<=n;j++) printf("%lld ",ans.a[i][j]); printf("\n"); }return 0;}
筛法求素数 :欧拉筛法
f[1]=1; for(int i=2;i<=n;i++) { if(!f[i]) p[++num]=i; for(int j=1;j<=num;j++) { if(p[j]*i>n)break; f[p[j]*i]=1; if(i%p[j]==0)break; } }
最大公约数和最小公倍数
gcd为最大公约数,lcm为最小公倍数
a∗b=gcd∗lcm;
int gcd(int a,int b)//注意此处a要大于b{ return b==0?a:gcd(b,a%b);}int lcm(int a,int b){ return a*b/gcd(a,b);}
扩展欧几里德
求ax+by=c的特解<=>求
int x,y;int exgcd(int a,int b){ int ret,t; if(b==0) { x=1; y=0; return a; } ret=gcd(b,a%b); t=y; y=x-(a%b)*y; x=t; return ret;}
欧拉函数(PHI φ)
高效算法同时打出prime表与phi表,比单独用筛法打出phi表与prime表快3倍左右
void prime_phi_table(){ int i,j; phi[1] = 1; for(i = 2;i <= MAXN;i++) { if(!vis[i]) { prime[++cnt] = i; phi[i] = i-1; } for(j = 1;j <= cnt && i * prime[j] <= MAXN;j++) { vis[i * prime[j]] = true; if(i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; } else phi[i * prime[j]] = phi[i] * (prime[j] - 1); } }}
求n的欧拉函数值
int phi(int n) { int t = sqrt(n + 0.5); int ans = 1; for(int i = 2;i <= t;i++) { if(n % i == 0) { ans *= (i-1); n /= i; while(n % i == 0) { ans *= i; n /= i; } } if(n == 1) break; } if(n > 1) ans *= (n-1); return ans; }
逆元
1. 扩展欧几里德求解
ab≡1(modp) ⇒ ab−1≡0(modp) ⇒ ab+pt=1 故可以用扩展欧几里德求解
#include <iostream>#include <cstdio>using namespace std;long long x,y,a,b,ans;long long gcd(long long a,long long b){ long long t,ret; if(b==0) { x=1; y=0; return a; } ret=gcd(b,a%b); t=y; y=x-(a/b)*y; x=t; return ret;}int main(){ cin>>a>>b; ans=gcd(a,b); while(x>b)x-=b; while(x<0)x+=b; cout<<x<<endl; return 0;}
2. 递推
for(int i=1;i<=maxn;i++)inv[i]=(long long)(p-(p/i))*inv[p%i]%p;
Catalan数
原理理解:(两个应用模板)
括号化
矩阵连乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?(h(n-1)种)
出栈次序
一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?
详情请参照百度百科
int main() // 求第n个卡特兰数{ cin>>n; h[0]=1;h[1]=1; for(int i=2;i<=n;i++) for(int j=0;j<=i-1;j++) h[i]=h[i]+h[j]*h[i-j-1]; cout<<h[n]; return 0;}
高精
读入、储存与输出
以下代码块均包含以下语句 :
int s[255];//255位的数//读入与储存 :void read(){ char ss[255]; scanf("%s",ss); for(int i=1;i<=strlen(ss);i++) s[i]=ss[strlen(ss)-i+1]-'0'; s[0]=strlen(ss);//存长度}//输出 :void print(){ for(int i=s[0];i>=1;i++) cout<<s[i];}
高精度加法
高精加单精
void add(int *s,int x)//s存高精度数,x是要加的单精度{ int k=1; s[k]+=x; while(s[k]>=10) { s[k+1]+=s[k]/10; s[k++]%=10; } s[0]=k;}
高精加高精
void add(int *s1,int *s2,int *ans){ int len; len=max(s1[0],s2[0]); for(int i=1;i<=len;i++) { ans[i]+=s1[i]+s2[i]; if(ans[i]>=10) { ans[i+1]+=ans[i]/10; ans[i]%=10; } } if(ans[len+1]!=0)len++; ans[0]=len;}
高精度乘法
高精乘单精
void multiply(int *s,int x){ for(int i=1;i<=n;i++) { c[i]+=s[i]*x; c[i+1]+=(s[i]*b)/10; c[i]%=10; } c[0]=s[0]+1; while(c[c[0]]>=10) { c[c[0]+1]+=c[c[0]]/10; c[c[0]]%=10; c[0]++; } while(c[0]>1&&c[c[0]]==0) { c[0]--; }}
高精乘高精
void multiply(int *s1,int *s2,int *ans){ for(int i=1;i<=s1[0];i++) for(itn j=1;j<=s2[0];j++) { ans[i+j-1]+=s1[i]*s2[j]; ans[i+j]+=ans[i+j-1]/10; ans[i+j-1]%=10; } ans[0]=s1[0]+s2[0]+1; while(ans[0]>1&&ans[ans[0]]==0) { ans[0]--; }}
高精度除法
放弃吧!
压位
const int opt=100000000;void multiply(int *num,int x){ for(int i=1;i<=num[0];i++)num[i]*=x; for(int i=1;i<=num[0];i++) { if(num[i]>=opt) { num[i+1]+=num[i]/opt; num[i]%=opt; }; while(num[num[0]]!=0) { num[0]++; }; }}void print(int *a){ for(int i=a[0];i>=1;i--) { if(i==a[0]) { cout<<a[i]; }else { if(a[i]<10000000)cout<<0; if(a[i]<1000000)cout<<0; if(a[i]<100000)cout<<0; if(a[i]<10000)cout<<0; if(a[i]<1000)cout<<0; if(a[i]<100)cout<<0; if(a[i]<10)cout<<0; cout<<a[i]; }; };}
⚠️:使用压位的时候的读入不要读错
比如不要把99存到数组的两个位置里面,而应该是一个;
图论
最短路
单源最短路SPFA
以下代码中包括邻接表(前向星)存图
#include<iostream>#include<cstdio>#define maxm 500005#define maxn 10005#define INF 0x7fffffffusing namespace std;int n,m,s;struct Edge{ int next; int to; int w;}e[maxm] ;int q[maxm],h[maxn],ok[maxn],dis[maxn];int p;void add_edge(int from ,int to,int w){ e[++p].next=h[from]; e[p].to=to; e[p].w=w; h[from]=p;}int tail,head;void SPFA(){ for(int i=1;i<=n;i++) dis[i]=INF; dis[s]=0; ok[s]=1; q[tail++]=s; while(head<tail) { int u=q[head++]; ok[u]=0; for(int i=h[u];i;i=e[i].next) { int v=e[i].to; if(dis[u]+e[i].w<dis[v]) { dis[v]=dis[u]+e[i].w; if(!ok[v]) { q[tail++]=v; ok[v]=1; } } } }}int main(){ scanf("%d%d%d",&n,&m,&s); for(int i=1;i<=m;i++) { int from,to,w; scanf("%d%d%d",&from,&to,&w); add_edge(from,to,w); } SPFA(); for(int i=1;i<=n;i++) { printf("%d ",dis[i]); } return 0;}
双端队列优化版
#include<iostream>#include<cstdio>#include<cmath>#include<queue>#include<vector>#include<cstdlib>#include<cstring>using namespace std;struct qq{ int t,w;}c;int s,n,m,u;int dis[10005];vector<qq>e[10005];bool vis[10005]; deque<int>q;void spfa(){ for(int i=1;i<=n;i++)dis[i]=2147483647; dis[s]=0;q.clear(); q.push_back(s); while(!q.empty()){ u=q.front();q.pop_front();vis[u]=0; for(int i=0;i<e[u].size();i++){ c=e[u][i]; if(dis[c.t]>dis[u]+c.w){ dis[c.t]=dis[u]+c.w; if(!vis[c.t]){ vis[c.t]=1; if(q.empty()||dis[q.front()]<dis[c.t])q.push_back(c.t); else q.push_front(c.t); } } } } for(int i=1;i<=n;i++)printf("%d ",dis[i]);}int main(){ scanf("%d%d%d",&n,&m,&s); for(int i=1;i<=m;i++){ scanf("%d%d%d",&u,&c.t,&c.w); e[u].push_back(c); } spfa(); return 0;}
copy by lijialin
多源最短路FLOYD
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define INF 10000100using namespace std;int a[101],d[105][105];int arrive[10005];int main(){ int n,m,ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&d[i][j]); for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { d[i][j]=min(d[i][j],d[i][k]+d[k][j]); } scanf("%d",&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); printf("%d\n",d[u][v]); } return 0;}
次短路
代码比较麻烦,不写了,说一下思路吧。
先用SPFA跑一遍,找出来最短路。把最短路记下来。
接下来,每次删掉最短路上的一条边,再跑一边SPFA。
运行一遍以后,路径长度最小的即次短路。
最小生成树(MST)
最小生成树即一个无向连通不含圈图中,连接G中所有点,且边集是E的子集,且边权和最小的生成树。(我解释的有点拗口)
最小生成树算法一共有两个:Prim和Kruskal算法,由于经并查集优化的Kruskal算法比Prim算法优秀得多,且Prim算法较容易理解,这里只给出Kruscal算法的模板。
Kruskal
下面展现两种做法,一种是普通的暴力枚举做法,另一种是并查集优化过的。并查集优化过的算法比较快,但是要忽略生成树的形状。就是说如果你需要用到新生成树的形状,那么不能使用此种方法。
1.普通方法:类似Prim算法
struct node{int u,v,w;}e[maxe];//u是起点,v是终点,w是权node MST[maxe];bool com(node a,node b){return a.w<b.w;};void Kruskal(){ sort(e+1,e+m+1,com);//按边权从小到大排序 for(int i=1;i<=m;i++) { if(!b[e[i].u]&&!b[e[i].v])//b判断是否已经在集合里 MST[++tot]=e[i]; };}
以上版本是自己写的,感觉不对,于是抄下来了粉书上的伪代码和讲解:
把所有边排序,记第i小的边为e[i](1<=i<m)
初始化MST为空
初始化连通分量,让每个点自成一个独立的连通分量
for(int i=1;i<=m;i++) if(e[i].u和e[i].v不在同一个连通分量) { 把边e[i]加入MST 合并e[i].u和e[i].v所在的连通分量 }
在上面的伪代码中,最关键的地方在于”连通分量的查询与合并”:需要知道任意两个点是否在同一个连通分量中,还需要合并两个连通分量。
最容易想到的方法是”暴力”——每次”合并”时只在MST中加入一条边(如果使用邻接矩阵,只需G[e[i].u][e[i].v]=1),而”查询”时直接在MST中进行图遍历(DFS和BFS都可以判断连通性)。
2.使用并查集优化
int find(int x){return p[x]==x?x:p[x]=find(p[x]);}//并查集的find和路径压缩int Kruskal()//返回的最小生成树的边权和{ int ans=0; for(int i=1;i<=n;i++)p[i]=i;//初始化并查集 sort(edge+1,edge+m+1,com);//给边从小到大排序 for(int i=1;i<=m;i++) { int x=find(edge[i].u); int y=find(edge[i].v); if(x!=y) { ans+=edge[i].w;//求和 p[x]=y; };//如果在不同的集合,合并 } return ans;}
其实此处还有一个优化,虽然不会节省很长时间,但是,优势都是一点点积累出来的!就是循环枚举边的时候不用for而用while,当当前得到的最小生成树一共有n-1条边时,最小生成树就已经生成完了,剩下的边就不用再枚举了。
TARJIAN
tarjian
图的遍历
还有一点相关的东西就是传递闭包(Transitive Closure)
即在有向图中,有时不必关心路径长度,而只关心每两点间是否有通路,则可以用1和0分别表示”连通”和”不连通”。得到的结果称为有向图的传递闭包。
只需将FLOYD程序中的 d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
改为 d[i][j]=d[i][j]||(d[i][k]&&d[k][j]));
建树
具体思路:对于一个节点来说,其他的任意一个节点,不是他的父节点,就是他的子节点。
传递闭包
详见上面图论部分Floyd算法。
LCA
#include<bits/stdc++.h>#define MN 500005using namespace std;int m,n,s,tot,dep[MN],up[MN][20];struct lll { int to; lll *ne; }a[2*MN];lll *head[MN];void DFS(int u){ for(int i=1;(1<<i)<=n;i++) { up[u][i]=up[up[u][i-1]][i-1]; } for(lll *e=head[u];e;e=e->ne) { if(dep[e->to]==-1) { dep[e->to]=dep[u]+1; up[e->to][0]=u; DFS(e->to); } } return;}int LCA(int u,int v){ if(dep[v]>dep[u]) swap(u,v); for(int i=19;i>=0;i--) { if(dep[up[u][i]]>=dep[v]) u=up[u][i]; } if(u==v) return v; for(int i=19;i>=0;i--) { if(up[u][i]!=up[v][i]) { u=up[u][i]; v=up[v][i]; } } return up[u][0];}int main(){ scanf("%d%d%d",&n,&m,&s); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); a[++tot].ne=head[x]; head[x]=&a[tot]; a[tot].to=y; a[++tot].ne=head[y]; head[y]=&a[tot]; a[tot].to=x; } memset(dep,-1,sizeof(dep)); dep[s]=0; up[s][0]=0; DFS(s); for(int j=1;j<=m;j++) { int u,v; scanf("%d%d",&u,&v); printf("%d\n",LCA(u,v)); }}
copy by RWZ
线段树
#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>#define LL long longusing namespace std;struct cs{ LL ll,rr,vv;}T[824290];LL a[200005],v[824290];LL n,m,x,y,z,sum,N;void clean(LL x){ if(!v[x])return; T[x].vv+=(T[x].rr-T[x].ll+1)*v[x]; if(T[x].ll!=T[x].rr) { v[x*2]+=v[x]; v[x*2+1]+=v[x]; } v[x]=0;}{ T[num].ll=x; T[num].rr=y; if(x==y) T[num].vv=a[x],return; maketree(x,(x+y)/2,num*2); maketree((x+y)/2+1,y,num*2+1); T[num].vv=T[num*2].vv+T[num*2+1].vv;}void inc(LL x,LL y,LL z,LL num) { clean(num); if(x<=T[num].ll&&T[num].rr<=y) v[num]+=z, return; T[num].vv+=(min(y,T[num].rr)-max(x,T[num].ll)+1)*z; if(T[num].ll==T[num].rr) return; int mid=(T[num].ll+T[num].rr)/2; if(x>mid)inc(x,y,z,num*2+1); else if(y<=mid)inc(x,y,z,num*2);else { inc(x,y,z,num*2); inc(x,y,z,num*2+1); }}void out(int LL,int LL,LL num){ clean(num); if(x<=T[num].ll&&T[num].rr<=y) sum+=T[num].vv,return; int mid=(T[num].ll+T[num].rr)/2; if(x>mid)out(x,y,num*2+1); else if(y<=mid)out(x,y,num*2); else { out(x,y,num*2); out(x,y,num*2+1); }}int main(){ scanf("%d%d",&n,&N); for(int i=1;i<=n;i++)scanf("%d",&a[i]); maketree(1,n,1); for(int i=1;i<=N;i++) { scanf("%d%d%d",&m,&x,&y); if(m==1) { scanf("%d",&z); inc(x,y,z,1); } else { sum=0; out(x,y,1); printf("%lld\n",sum);//用cout输出什么事都没有 } }}
树状数组
比线段树短啊
1. 单点修改,区间查询(codevs1080 线段树练习)
单点修改:
#define lowbit(x) x&(~x+1)using namespace std;int a[MAXN],tree[MAXN],n,q;void add(int k,int x){ while(k <= n) { tree[k] += x; k += lowbit(k); }}
区间查询:
int read(int k){ int sum = 0; while(k) { sum += tree[k]; k -= lowbit(k); } return sum;}/*printf("%d\n",read(b) - read(a-1));//输出
2.区间修改,单点查询(codevs1081 线段树练习 2)
修改和查询函数与上面是一样的,不过预处理的时候有所不同,这里只放主函数代码
int main(){ scanf("%d",&n); for(int i = 1; i <= n; i ++) { scanf("%d",&a[i]); add(i,a[i]); add(i+1,-a[i]); } scanf("%d",&q); int t; for(int i = 1; i <= q; i ++) { scanf("%d",&t); if(t == 1) { int a,b,x; scanf("%d%d%d",&a,&b,&x); add(a,x); add(b+1,-x); } if(t == 2) { int a; scanf("%d",&a); printf("%d\n",read(a)); } } return 0;}
3. 区间修改,区间查询(codevs1082 线段树练习 3)
#include <cstdio>#include <algorithm>#include <cstring>#include <iostream>#define MAXN 200000+5#define lowbit(x) x&(~x+1)using namespace std;typedef long long ll;ll a[MAXN],d1[MAXN],d2[MAXN],n,q;void add(ll *d,ll k,ll x){ while(k <= n) { d[k] += x; k += lowbit(k); }}ll read(ll *d,ll k){ ll sum = 0; while(k) { sum += d[k]; k -= lowbit(k); } return sum;}int main(){ scanf("%lld",&n); for(int i = 1; i <= n; i ++) { scanf("%lld",&a[i]); add(d1,i,a[i]); add(d1,i+1,-a[i]); add(d2,i,i*a[i]); add(d2,i+1,-a[i]*(i+1)); } scanf("%lld",&q); int t; for(int i = 1; i <= q; i ++) { scanf("%d",&t); if(t == 1) { int a,b,x; scanf("%d%d%d",&a,&b,&x); add(d1,a,x); add(d1,b+1,-x); add(d2,a,x*a); add(d2,b+1,-x*(b+1)); } if(t == 2) { int a,b; scanf("%d%d",&a,&b); printf("%lld\n",((b+1)*read(d1,b)-read(d2,b))-(a*read(d1,a-1)-read(d2,a-1))); } } return 0;}
并查集
#include<iostream>#include<cstdio>using namespace std;int x,y,n,m,q;int fa[20020];int find (int x){ if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x];}void unionn(int x,int y){ int r1=find(x),r2=find(y); if(r1!=r2) fa[r2]=r1; } int main(){ scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;i++) { fa[i]=i; } for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); unionn(x,y); } for(int i=1;i<=q;i++) { scanf("%d%d",&x,&y); if(find(x)==find(y)) printf("Yes\n"); else printf("No\n"); } return 0;}
字符串
KMP
KMP1 KMP2 KMP3
TRIE树
TRIE1 TRIE2
MANACHER
#include<iostream>#include<cmath>#include<cstring>#define maxn 51000100using namespace std;int n,hw[maxn],ans;char a[maxn],s[maxn<<2];//因为要插入字符所以*2,空间限制也很安全void manacher(){ int maxright=0,mid; for(int i=1;i<n;i++) { if(i<maxright) hw[i]=min(hw[2*mid-i],hw[mid]+mid-i);//这句话重点,好好理解 else hw[i]=1; for(;s[i+hw[i]]==s[i-hw[i]];++hw[i]);//继续扩展 if(hw[i]+i>maxright) { maxright=hw[i]+i;//更新 mid=i; } }}void change(){ s[0]=s[1]='#'; for(int i=0;i<n;i++) { s[i*2+2]=a[i]; s[i*2+3]='#'; } n=n*2+2; s[n]=0;}int main(){ scanf("%s",a); n=strlen(a); change(); manacher(); ans=1; for(int i=0;i<n;i++) ans=max(ans,hw[i]); printf("%d",ans-1);//想想为什么要-1 return 0; }
动态规划
钟长者说:有几个未知量,DP数组就有几维,若求个数能再省掉最后一维。
然而这只是一般情况,例如有个例外:HAOI2012 音量调节/Luogu P1877,这道题就不能省掉最后一维。
铭哥说:重推所有的DP方程是复习DP的最佳方法
01背包
for(int i=1;i<=m;i++)//m个物品 for(int j=1;j<=w;j++)//背包容量为w if(a[i]<=j) { dp[i][j]=max(dp[i-1,j-a[i]]+val[i],dp[i-1,j]); //a数组是占用的容量,val是价值 }else { dp[i][j]=dp[i-1][j]; };//此时dp[m][w]即为最大权值优化://条件和上面一样for(int i=1;i<=n;i++) for(int j=w;i>=a[i];j--) dp[j]=(dp[j],dp[j-a[i]]+val[i]);
完全背包
//条件和上面一样,只是每个物品可以取无数次for(int i=1;i<=n;i++) for(int j=a[i];i<=w;j++)//注意这里的改动 dp[j]=(dp[j],dp[j-a[i]]+val[i]);
分组背包(多维背包)(多重限制背包(迷))
即有好几种背包的条件,分别写dp满足条件就可以了
其他模板
归并排序(逆序对)
#include<iostream>#include<cstdio>#define MAXN 100005using namespace std;int a[MAXN],tmp[MAXN];int ans;void mergesort(int l,int r){ if(l==r) return; int mid=(l+r)/2; mergesort(l,mid); mergesort(mid+1,r); int i=l,j=mid+1,pos=l; while (i<=mid&&j<=r) { if(a[i]<=a[j]) { tmp[pos++]=a[i++]; } else { tmp[pos++]=a[j++]; ans+=(mid-i+1); } } while (i<=mid)tmp[pos++]=a[i++]; while (j<=r)tmp[pos++]=a[j++]; for(int i=l;i<=r;i++) { a[i]=tmp[i]; } return ;} int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } mergesort(1,n); cout<<ans; return 0;}
二分
void binary(int l,int r) //找最小{ while(r>l) { mid=(l+r)/2; if(check(mid))r=mid; else l=mid+1; } ans=l;}void binary(int l,int r) //找最大{ while(r>l) { mid=(r+l)/2+1; //特别注意 if(check(mid))l=mid+1; else r=mid; } ans=l;}
网络流(dinic)
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define inf 0x7fffffff#define MAXN 10005#define MAXM 100005using namespace std;struct EDGE{ int next,to,w;}e[MAXM<<1];int p;int q[MAXM+MAXN],h[MAXM];int deep[MAXN];inline void add_edge(int from,int to,int w){ e[++p].next=h[from]; e[p].to=to; e[p].w=w; h[from]=p;}int s,t;inline bool bfs(){ memset(deep,0,sizeof(deep)); int head=0,tail=0; q[++tail]=s; deep[s]=1; while(head<tail) { int now=q[++head]; for(int i=h[now];i;i=e[i].next) { int v=e[i].to; if(!deep[v]&&e[i].w) { deep[v]=deep[now]+1; q[++tail]=v; if(v==t) return true; } } } return false;}inline int dfs(int now,int cur_flow){ if(now==t) return cur_flow; int rest=cur_flow; for(int i=h[now];i;i=e[i].next) { int v=e[i].to; if(deep[v]==deep[now]+1&&rest&&e[i].w) { int new_flow=dfs(v,min(rest,e[i].w)); rest-=new_flow; e[i].w-=new_flow; (i%2)?(e[i+1].w+=new_flow):(e[i-1].w+=new_flow); } } return cur_flow-rest;}inline int dinic(){ int ans=0; while(bfs()) { ans+=dfs(s,inf); } return ans;}int main(){ int n,m; cin>>n>>m>>s>>t; int from,to,w; for(int i=1;i<=m;i++) { scanf("%d%d%d",&from,&to,&w); add_edge(from,to,w); add_edge(to,from,0); } cout<<dinic(); return 0;}
最长上升子序列
把楼下离散化去掉即可
最长公共子序列
离散化+最长上升子序列
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;int n;int a1[100010],a2[100010];int belong[100010];int f[100010],b[100010],len;int main(){ freopen("a.in","r",stdin); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a1[i]); belong[a1[i]]=i; } for(int i=1;i<=n;i++) scanf("%d",&a2[i]); for(int i=1;i<=n;i++) { if(belong[a2[i]]>b[len]) { b[++len]=belong[a2[i]]; f[i]=len; continue; } int k=lower_bound(b+1,b+len+1,belong[a2[i]])-b; b[k]=belong[a2[i]]; f[i]=k; } printf("%d\n",len); return 0;}
RMQ(区间最值)
#include<iostream>#include<cstdio>#define MAXN 100005using namespace std;int n,m;long long a[MAXN],d[MAXN][18];inline int read (){ int n=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') { n=n*10+ch-'0'; ch=getchar(); } return n;}inline void RMQ_pre(){ for(int i=1;i<=n;i++) { d[i][0]=read(); } for(int j=1;(1<<j)<=n;j++)//(1<<j)<=n for(int i=1;i+(1<<j)-1<=n;i++)//i+(1<<j)-1<=n d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]);} inline int RMQ(int l,int r){ int k=0; while((1<<k)<=r-l+1) k++; k--; return max(d[l][k],d[r-(1<<k)+1][k]);}int main(){ n=read(); m=read(); RMQ_pre(); for(int i=1;i<=m;i++) { int l=read(),r=read(); printf("%d\n",RMQ(l,r)); } return 0;}
TREAP
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
插入x数
删除x数(若有多个相同的数,因只删除一个)
查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
查询排名为x的数
求x的前驱(前驱定义为小于x,且最大的数)
求x的后继(后继定义为大于x,且最小的数)
#include <cstdio> #include <cstdlib>#include <ctime>#define N 500005using namespace std;int inline read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}int ch[N][2],val[N],pri[N],siz[N],sz;void update(int x){siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];}int new_node(int v){ siz[++sz]=1; val[sz]=v; pri[sz]=rand(); return sz;}int merge(int x,int y){ if (!x || !y) return x+y; if (pri[x]<pri[y]) { ch[x][1]=merge(ch[x][1],y); update(x); return x; } else { ch[y][0]=merge(x,ch[y][0]); update(y); return y; }}void split(int now,int k,int &x,int &y){ if (!now) x=y=0; else { if (val[now]<=k) x=now,split(ch[now][1],k,ch[now][1],y); else y=now,split(ch[now][0],k,x,ch[now][0]); update(now); }}int kth(int now,int k){ while(1) { if (k<=siz[ch[now][0]]) now=ch[now][0]; else if (k==siz[ch[now][0]]+1) return now; else k-=siz[ch[now][0]]+1,now=ch[now][1]; }}int main(){ //srand((unsigned)time(NULL)); int T,com,x,y,z,a,b,root=0; scanf("%d",&T); while(T--) { com=read(),a=read(); if (com==1) { split(root,a,x,y); root=merge(merge(x,new_node(a)),y); } else if (com==2) { split(root,a,x,z); split(x,a-1,x,y); y=merge(ch[y][0],ch[y][1]); root=merge(merge(x,y),z); } else if (com==3) { split(root,a-1,x,y); printf("%d\n",siz[x]+1); root=merge(x,y); } else if (com==4) printf("%d\n",val[kth(root,a)]); else if (com==5) { split(root,a-1,x,y); printf("%d\n",val[kth(x,siz[x])]); root=merge(x,y); } else { split(root,a,x,y); printf("%d\n",val[kth(y,1)]); root=merge(x,y); } } return 0;}
匈牙利算法
bool dfs(int now){ for (int a=1;a<=m;a++) if (match[now][a] && !use[a]) { use[a]=true; if (!result[a] || dfs(result[a])) { result[a]=now; return true; } } return false;}void xiongyali() { int ans=0; for (int a=1;a<=n;a++) { memset(use,false,sizeof(use)); if (dfs(a)) ans++; }}
对于变式题
ans
n-ans
m-ans
n+m-ans
总有一个是答案
- 冲刺NOIp2016算法模板
- 冲刺NOIp2016算法模板(C++)
- [ 冲刺NOIP2016 ] 高精度模板
- [ 冲刺NOIP2016 ] 复习计划
- NOIP 冲刺之manacher模板
- NOIP 冲刺 模板:树的直径
- 冲刺NOIP复习,算法知识点总结
- [XJOI NOIP2016提高组冲刺题1] 排队:倍增,set or 线段树
- NOIP2016
- NOIP2016
- NOIP2016
- noip2016
- noip2016
- NOIP2016
- noip2016
- noip2016
- 蓝桥杯大赛java组算法类冲刺第一天
- JZOJ 4676. 【NOIP2016提高A组模拟7.21】模板串
- 进制转换
- PTA 家谱处理
- size_type
- matlab的计时函数tic toc cputime和etime
- javascript之获取标签
- 冲刺NOIp2016算法模板
- Game Of Sum(区间dp)
- java常用类
- ContentProvider
- SpringMVC学习笔记
- 【安全牛学习笔记】基本工具-tcpdump
- Servlet 生命周期及工作原理
- STL_set用法
- 我的学习记录30