马上就要考NOIP了,是时候整理一下模板了233
线段树模板
区间修改+区间求和:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
long long xl[233333];
struct jgt
{
int l,r;
long long sum,add;
}hah[233333*4];
void build(int p,int l,int r)
{
hah[p].l=l;
hah[p].r=r;
if(l==r)
{
hah[p].sum=xl[l];
return ;
}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
hah[p].sum=hah[p*2].sum+hah[p*2+1].sum;
}
void spread(int p)
{
if(hah[p].add)
{
hah[p*2].sum+=(hah[p*2].r-hah[p*2].l+1)*hah[p].add;
hah[p*2+1].sum+=(hah[p*2+1].r-hah[p*2+1].l+1)*hah[p].add;
hah[p*2].add+=hah[p].add;
hah[p*2+1].add+=hah[p].add;
hah[p].add=0;
}
}
long long ask(int p,int l,int r)
{
if(l<=hah[p].l&&hah[p].r<=r)
{
return hah[p].sum;
}
spread(p);
long long ans=0;
int mid=(hah[p].l+hah[p].r)/2;
if(l<=mid) ans+=ask(p*2,l,r);
if(mid<r) ans+=ask(p*2+1,l,r);
return ans;
}
void change(int p,int l,int r,long long x)
{
if(l<=hah[p].l&&hah[p].r<=r)
{
hah[p].sum+=(hah[p].r-hah[p].l+1)*x;
hah[p].add+=x;
return ;
}
spread(p);
int mid=(hah[p].l+hah[p].r)/2;
if(l<=mid) change(p*2,l,r,x);
if(mid<r) change(p*2+1,l,r,x);
hah[p].sum=hah[p*2].sum+hah[p*2+1].sum;
}
int main()
{
int n,q;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&xl[i]);
build(1,1,n);
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int u;
scanf("%d",&u);
if(u==1)
{
int a,b;
long long x;
scanf("%d%d%lld",&a,&b,&x);
change(1,a,b,x);
}
if(u==2)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%lld\n",ask(1,a,b));
}
}
return 0;
}
询问区间最值:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXN=233333;
int xl[MAXN];
struct jgt
{
int l,r,maxx,minn;
}hah[MAXN<<2];
void updata(int p)
{
hah[p].maxx=max(hah[p<<1].maxx,hah[p<<1|1].maxx);
hah[p].minn=min(hah[p<<1].minn,hah[p<<1|1].minn);
}
void build(int p,int l,int r)
{
hah[p].l=l;
hah[p].r=r;
if(l==r)
{
hah[p].maxx=hah[p].minn=xl[l];
return ;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
updata(p);
}
int askmax(int p,int l,int r)
{
if(l<=hah[p].l&&hah[p].r<=r)
return hah[p].maxx;
int mid=(hah[p].l+hah[p].r)>>1;
int ans=0;
if(l<=mid) ans=max(ans,askmax(p<<1,l,r));
if(mid<r) ans=max(ans,askmax(p<<1|1,l,r));
return ans;
}
int askmin(int p,int l,int r)
{
if(l<=hah[p].l&&hah[p].r<=r)
return hah[p].minn;
int mid=(hah[p].l+hah[p].r)>>1;
int ans=233333333;
if(l<=mid) ans=min(ans,askmin(p<<1,l,r));
if(mid<r) ans=min(ans,askmin(p<<1|1,l,r));
return ans;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&xl[i]);
build(1,1,n);
printf("%d %d\n",askmax(1,1,n),askmin(1,1,n));
return 0;
}
最短路模板
spfa(Shortest Path Faster Algorithm强行装逼╮(╯▽╰)╭):
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct edge
{
int from,to,cost;
}es[MAXE<<1];
void init()
{
memset(first,-1,sizeof(first));
memset(d,63,sizeof(d));
tot=0;
}
void build(int f,int t,int d)
{
es[++tot]=(edge){f,t,d};
nxt[tot]=first[f];
first[f]=tot;
}
queue<int> Q;
void spfa(int s)
{
d[s]=0;
Q.push(s);
used[s]=1;
while(!Q.empty())
{
int u=Q.front();
Q.pop();
used[u]=0;
for(int i=first[u];i!=-1;i=nxt[i])
{
int v=es[i].to;
if(d[v]>d[u]+es[i].cost)
{
d[v]=d[u]+es[i].cost;
if(!used[v])
{
Q.push(v);
used[v]=1;
if(++time[v]>n)//判负环
{
flag=1;
return ;
}
}
}
}
}
}
/*---------------华丽的分割线---------------*/
deque<int> Q;//slf(双端队列优化)
void spfa(int s)
{
d[s]=0;
Q.push_front(s);
used[s]=1;
while(!Q.empty())
{
int u=Q.front();
Q.pop_front();
used[u]=0;
for(int i=first[u];i!=-1;i=nxt[i])
{
int v=es[i].to;
if(d[v]>d[u]+es[i].cost)
{
d[v]=d[u]+es[i].cost;
if(!used[v])
{
used[v]=1;
if(++time[v]>n)//判负环
{
flag=1;
return ;
}
if(Q.empty()) Q.push_back(v);
else if(d[Q.front()]>d[v]) Q.push_front(v);
else Q.push_back(v);
}
}
}
}
}
int main()
{
......
return 0;
}
dijkstra(听说dijkstra能求第k短路所以还是有必要学一下):
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct edge
{
int from,to,cost;
}es[MAXE<<1];
struct node
{
int num,dis;
bool operator < (node b) const
{
return dis>b.dis;
}
};
void init()
{
memset(first,-1,sizeof(first));
memset(dis,63,sizeof(dis));
tot=0;
}
void build(int f,int t,int d)
{
es[++tot]=(edge){f,t,d};
nxt[tot]=first[f];
first[f]=tot;
}
void dijkstra(int s)
{
d[s]=0;
while(true)
{
int v=-1;
for(int i=1;i<=n;i++)
if((v==-1||d[i]<d[v])&&!used[i]) v=i;
if(v==-1) return ;
used[v]=1;
for(int i=first[v];i!=-1;i=nxt[i])
{
int u=es[i].to;
if(d[u]>d[v]+es[i].cost)
d[u]=d[v]+es[i].cost;
}
}
}
/*---------------华丽的分割线---------------*/
priority_queue<node> Q;//加了堆优化
void dijkstra(int s)
{
d[s]=0;
Q.push((node){s,d[s]});
while(!Q.empty())
{
node u=Q.front();
Q.pop();
used[u.num]=1;
for(int i=first[u.num];i!=-1;i=nxt[i])
{
int v=es[i].to;
if(d[v]>u.dis+es[i].cost)
{
d[v]=u.dis+es[i].cost;
if(!used[v]) Q.push((node){v,d[v]});
}
}
}
}
int main()
{
......
return 0;
}
floyd(这是个非常神奇的最短路算法,有很多拓展,然而我不会╮(╯▽╰)╭):
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
void floyd
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++) if(k!=i)
for(int j=1;j<=n;j++) if(i!=j)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
int main()
{
......
return 0;
}
并查集模板
并查集:(带路径压缩):
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
void init()
{
for(int i=1;i<=n;i++) fa[i]=i;//初始化
}
void find(int x,int y)//查询
{
return fa[x]==x?x:fa[x]=find(fa[x]);//自带路径压缩╮(╯▽╰)╭
}
void merge(int x,int y)//合并
{
int f1=find(x);
int f2=find(y);
if(f1!=f2) fa[f1]=f2;
}
int main()
{
......
return 0;
}
最小生成树模板
kruskal(适用于稀疏图,听cy学长说能跟01分数规划搞一搞╮(╯▽╰)╭):
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int tot=0;
struct edge
{
int from,to,cost;
}es[MAXE<<1];
void build(int f,int t,int d)
{
es[++tot]=(edge){f,t,d};
}
bool cmp(edge a,edge b)
{
return a.cost<b.cost;
}
void kruskal()
{
int cnt=0;
sort(es+1,es+tot+1,cmp);
for(int i=1;i<=tot;i++)
{
int f1=find(es[i].from);
int f2=find(es[i].to);
if(f1!=f2)
{
fa[f1]=f2;
ans+=es[i].cost;
if(++cnt==n-1) break;
}
}
}
int main()
{
......
return 0;
}
prim(适用于稠密图,据说加了堆优化之后跑的比kruskal快,然并卵╭(╯^╰)╮)
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int inf=0x7fffffff;
int first[30100],nxt[366100];
int n,tot,ans=0,d[30100];
bool vis[30100];
struct edge
{
int from,to,cost;
}es[366100];
void init()
{
memset(first,-1,sizeof(first));
tot=0;
}
void build(int f,int t,int d)
{
es[++tot]=(edge){f,t,d};
nxt[tot]=first[f];
first[f]=tot;
}
void prim()
{
int u;
vis[1]=1;
for(int i=first[1];i!=-1;i=nxt[i])
d[es[i].to]=es[i].cost;
for(int i=1;i<=n;i++)
{
int minn=inf;
for(int j=2;j<=n;j++)
{
if(vis[j]) continue;
if(d[j]<minn)
{
minn=d[j];
u=j;
}
}
if(minn==inf) return ;
ans+=minn;
vis[u]=1;
for(int j=first[u];j!=-1;j=nxt[j])
{
int v=es[j].to;
if(!vis[v]&&es[j].cost<d[v])
d[v]=es[j].cost;
}
}
}
int main()
{
......
prim();
......
return 0;
}
Tarjan模板
Tarjan求scc(我现在只会这个╮(╯▽╰)╭):
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXV=100100;
const int MAXE=100100;
int first[MAXV],nxt[MAXE<<1];
int dfn[MAXV],low[MAXV];
int stack[MAXV],scc[MAXV],size[MAXV],du[MAXV];
int n,m,tot,tot1,snum,cnt;
bool in_stack[MAXV];
struct edge
{
int from,to;
}es[MAXE<<1];
void init()
{
memset(first,-1,sizeof(first));
tot=0;
}
void build(int f,int t)
{
es[++tot]=(edge){f,t};
nxt[tot]=first[f];
first[f]=tot;
}
void group(int u)
{
dfn[u]=low[u]=++tot1;
stack[++snum]=u;
in_stack[u]=1;
for(int i=first[u];i!=-1;i=nxt[i])
{
int v=es[i].to;
if(!dfn[v])
{
group(v);
low[u]=min(low[u],low[v]);
}
else if(in_stack[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
cnt++;
while(stack[snum+1]!=u)
{
scc[stack[snum]]=cnt;
in_stack[stack[snum]]=0;
size[cnt]++;
snum--;
}
}
}
int main()
{
......
for(int i=1;i<=n;i++)
if(!dfn[i]) group(i);
......
return 0;
}
拓扑排序模板
拓扑排序(可以找环之类的╮(╯▽╰)╭):
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m;
struct edge
{
int from,to,cost;
}es[MAXE<<1];
void init()
{
memset(first,-1,sizeof(first));
tot=0;
}
void build(int f,int t,int d)
{
es[++tot]=(edge){f,t,d};
nxt[tot]=first[f];
first[f]=tot;
}
queue<int> Q;
void top_sort()
{
for(int i=1;i<=n;i++)
if(!ru[i])
{
Q.push(i);
used[i]=1;
printf("%d ",i);
}
while(!Q.empty())
{
int u=Q.front();
Q.pop();
for(int i=first[u];i!=-1;i=nxt[i])
{
int v=es[i].to;
ru[v]--;
if(!ru[v])
{
printf("%d ",v);
Q.push(v);
used[v]=1;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
build(a,b);
ru[b]++;
}
top_sort();
return 0;
}
最近公共祖先模板
(倍增)LCA:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct edge
{
int from,to,cost;
}es[MAXE<<1];
void init()
{
memset(first,-1,sizeof(first));
tot=0;
}
void build(int f,int t,int d)
{
es[++tot]=(edge){f,t,d};
nxt[tot]=first[f];
first[f]=tot;
}
void dfs(int s)
{
used[s]=1;
for(int i=first[s];i!=-1;i=nxt[i])
{
int v=es[i].to;
if(!used[v])
{
deep[v]=deep[s]+1;
dis[v]=dis[s]+es[i].cost;//dis[v]=es[i].cost; //有多种方法存dis
fa[v]=s;//fa[v][0]=s; //暴力和倍增的fa数组
dfs(v);
}
}
}
void lca(int x,int y)//暴力lca
{
if(deep[x]>deep[y]) swap(x,y);
while(deep[x]!=deep[y])
{
y=fa[y];
}
while(x!=y)
{
x=fa[x];
y=fa[y];
}
return x;
}
/*---------------华丽的分割线---------------*/
void lca(int x,int y)//倍增lca
{
for(int i=0;i<n;i++)
for(int j=1;j<=20;j++)
fa[i][j]=fa[fa[i][j-1]][j-1];
if(deep[x]>deep[y]) swap(x,y);
int tt=deep[y]-deep[x];
for(int i=0;i<=20;i++)
if((1<<i)&tt) y=fa[y][i];
for(int i=20;i>=0;i--)
if(fa[y][i]!=fa[x][i])
{
x=fa[x][i];
y=fa[y][i];
}
if(x!=y) return fa[x][0];
else return x;
}
int main()
{
......
return 0;
}
数论模板
gcd(__gcd)&&lcm:
#include<cstdio>
#include<cstring>
#include<iostream>
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;
scanf("%d%d",&a,&b);
printf("%d %d\n",gcd(a,b),lcm(a,b));
return 0;
}
埃氏筛法(仅次于线性筛╮(╯▽╰)╭):
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
bool pri[1000100];
int su[1000100];
int main()
{
int n,tot=0;
scanf("%d",&n);
memset(pri,1,sizeof(pri));
pri[0]=0,pri[1]=0;
for(int i=2;i<=sqrt(n);i++)
{
if(pri[i])
{
tot++;
su[tot]=i;
for(int j=i*i;j<=n;j+=j) pri[j]=0;
}
}
return 0;
}
欧拉筛法(线性筛):
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int pri[10100];
bool vis[10100];
int main()
{
int n,cnt=0;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
if(!vis[i]) pri[++cnt]=i;
for(int j=1;j<=cnt&&i*pri[j]<=n;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
for(int i=1;i<=cnt;i++)
printf("%d ",pri[i]);
printf("\n");
return 0;
}
快速幂(非常实用的算法):
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
long long ksm(long long x,long long y,long long p)//递归版
{
if(y==0) return 1;
if(y==1) return x%p;
long long fx=ksm(x,y/2,p);
if(y%2==0) return (fx%p*fx%p)%p;
else return ((fx%p*fx%p)%p*x%p);
}
/*---------------华丽的分割线---------------*/
long long ksm2(long long a,long long k,long long p)//二进制版
{
long long ans=1;
for(;k;k>>=1,a=(a%p*a%p)%p)
if(k&1) ans=(ans%p*a%p)%p;
return ans%p;
}
int main()
{
int a,b,p;
scanf("%d%d%d",&a,&b,&p);
printf("%lld",ksm(a,b,p));
return 0;
}
其他模板
归并排序求逆序对:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int a[100100],zh[100100];
unsigned long long k=0;
void merge_sort(int l,int r)
{
if(l>=r) return ;
int mid=(l+r)/2;
merge(l,mid);
merge(mid+1,r);
int p=l,q=l,j=mid+1;
while(p<=mid&&j<=r)
{
if(a[p]>a[j])
{
k+=mid-p+1;
zh[q++]=a[j++];
}
else zh[q++]=a[p++];
}
while(p<=mid) zh[q++]=a[p++];
while(j<=r) zh[q++]=a[j++];
for(int i=l;i<=r;i++) a[i]=zh[i];
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
merge_sort(1,n);
cout<<k;
return 0;
}
状态压缩BFS(以codevs解药还是毒药为例):
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int bin[100],n,m,sum=0,tot=0;
int yao[110][110];//yao[i][j]表示第i种药对第j种病的影响
bool used[10100],flag;
struct anti
{
int sta[110],num;
}hah[10100];//队列
void init()
{
for(int i=1;i<=n;i++)
{
sum+=1<<i;//sum是最终状态,记录一下最终状态
bin[i]=1<<i;//预处理每一位修改需要加上的值
}
}
void bfs()
{
init();//初始化
int tt=0,ww=1;
while(tt<ww)
{
tt++;
for(int i=1;i<=m;i++)
{
int tot=0;
ww++;
for(int j=1;j<=n;j++)//从队列中取出的元素向下BFS
{
if(yao[i][j]==1) hah[ww].sta[j]=1;//如果能治好
else if(yao[i][j]==-1) hah[ww].sta[j]=0;//如果会得病
else hah[ww].sta[j]=hah[tt].sta[j];//如果是0就让他不变
if(hah[ww].sta[j]) tot+=bin[j];//当前位是一就加上预处理的需要加的数
}
if(!used[tot])//如果当前状态没有搜到过就加入队列
{
used[tot]=1;
hah[ww].num=hah[tt].num+1;//记录一下用的药的数量
if(tot==sum)//搜到最终状态就退出
{
printf("%d\n",hah[ww].num);
flag=1;
return ;
}
}
else ww--;//如果已经搜过就不需要加入队列
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
scanf("%d",&yao[i][j]);
bfs();
if(!flag) printf("The patient will be dead.\n");
return 0;
}