板子
typedef __int128_t lll;
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
getline(cin,name)
next_permutation(a,a+n)//a[n]={1,2,..,n},用一次,输出一次(会变成下个排列)O(n)
读入&输出优化
inline int read(){
int ret=0,k=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') k=-1;
c=getchar();
}
while(isdigit(c)){
ret=(ret<<1)+(ret<<3)+c-'0';
c=getchar();
}
return k*ret;
}
inline double dbread(){
double X=0,Y=1.0;bool w=false;char ch=0;
while(!isdigit(ch)){
w|=ch=='-';
ch=getchar();
}
while(isdigit(ch)) {
X=X*10+(ch^48);
ch=getchar();
}
ch=getchar();
while(isdigit(ch)){
X+=(Y/=10)*(ch^48);
ch=getchar();
}
return w?-X:X;
}
#define QWtype int
char qw[40];
void QW(QWtype x){
if(x==0){putchar('0');return;}
if(x<0) putchar('-');
int qwcnt=0; x=((x>0)?x:-x);
while(x>0){ qw[++qwcnt]=x%10+'0'; x/=10; }
while(qwcnt>0)putchar(qw[qwcnt--]);
}
二分
int l,r,mid;
while(l<r){
mid=l+r>>1;
if(chk(mid)) r=mid;
else l=mid+1ll;
}
//l
三分
int lef,rig,m1,m2;
while(lef<rig){
m2=(rig-lef+1)/3;m1=lef+m2;m2+=m1;
if(chk(m1,m2)) rig=m2-1;
else lef=m1+1;
}
//lef
离散化
sort(p+1,p+1+m);
m=unique(p+1,p+m+1)-p-1;
for(int i=1;i<=n;++i){
b[i]=lower_bound(p+1,p+1+m,b[i])-p;
}
折半搜索
inline ll search(ll m){
if(m<b[1].x) return 0;
int l=1,r=tot,mid;
while(l<r){
mid=l+r+1>>1;
if(b[mid].x<=m) l=mid;
else r=mid-1;
}
return b[l].w;
}
inline void dfs1(int u,ll sw,ll sx){
if(u>nn){
b[++cnt]=(thing){sx,sw};
return;
}
dfs1(u+1,sw,sx);
if(sx+a[u].x<=m)
dfs1(u+1,sw+a[u].w,sx+a[u].x);
}
inline void dfs2(int u,ll sw,ll sx){
if(u>n){
sw+=search(m-sx);
ans=max(ans,sw);
return;
}
dfs2(u+1,sw,sx);
if(sx+a[u].x<=m)
dfs2(u+1,sw+a[u].w,sx+a[u].x);
}
莫队
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200005,K=1000005;
struct query{
int l,r,n;
}q[N];
int a[N],s[K],f[N],n,m,t;
ll ans[N];
bool cmp(query a,query b){
if(f[a.l]!=f[b.l]) return f[a.l]<f[b.l];
return a.r<b.r;
}
ll add(int i){
++s[a[i]];
return 1ll*a[i]*((s[a[i]]<<1)-1);
}
ll remove(int i){
--s[a[i]];
return 1ll*a[i]*(s[a[i]]<<1|1);
}
int main(){
scanf("%d%d",&n,&t);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
for(int i=1;i<=t;++i){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].n=i;
}
m=sqrt(n);
for(int i=1,k=1;i<=n;++k)
for(int j=1;i<=n&&j<=m;++i,++j)
f[i]=k;
sort(q+1,q+1+t,cmp);
int l=1,r=1;
ll tmp=1ll*a[1];
++s[a[1]];
for(int i=1;i<=t;++i){
while(r<q[i].r){
++r;tmp+=add(r);
}
while(r>q[i].r){
tmp-=remove(r);--r;
}
while(l>q[i].l){
--l;tmp+=add(l);
}
while(l<q[i].l){
tmp-=remove(l);++l;
}
ans[q[i].n]=tmp;
}
for(int i=1;i<=t;++i)
printf("%lld\n",ans[i]);
return 0;
}
高精度
struct Number{
int a[N],n;bool POrN;
void init(){
memset(a,0,sizeof(a));
n=1;POrN=true;
}
void read(){
init();--n;
char c=getchar();
while(!isdigit(c)){
if(c=='-') POrN=false;
c=getchar();
}
while(isdigit(c)){
a[n++]=c-'0';c=getchar();
}
}
void write(){
if(!POrN) cout<<"-";
for(int i=0;i<n;++i) cout<<a[i];
}
Number abs(const Number a){
Number c=a;
c.POrN=true;return c;
}
friend bool operator < (const Number a,const Number b){
if(a.POrN^b.POrN) return !a.POrN;
if(a.n^b.n) return a.n<b.n;
for(int i=0;i<a.n;++i)
if(a.a[i]^b.a[i]) return a.a[i]<b.a[i];
return false;
}
friend bool operator <= (const Number a,const Number b){
if(a.POrN^b.POrN) return !a.POrN;
if(a.n^b.n) return a.n<b.n;
for(int i=0;i<a.n;++i)
if(a.a[i]^b.a[i]) return a.a[i]<=b.a[i];
return false;
}
friend bool operator > (const Number a,const Number b){
return b<a;
}
friend bool operator >= (const Number a,const Number b){
return b<=a;
}
void tran(){
for(int i=0;i<(n>>1);++i){
swap(a[i],a[n-i-1]);
}
}
Number add(Number a,Number b){
Number c;c.init();
c.n=max(a.n,b.n);
a.tran();b.tran();
for(int i=0;i<c.n;++i){
c.a[i]+=a.a[i]+b.a[i];
if(c.a[i]>=10){
c.a[i]-=10;++c.a[i+1];
}
}
while(c.a[c.n]) ++c.n;
c.tran();
return c;
}
Number sub(Number a,Number b){
Number c;c.init();
c.n=max(a.n,b.n);
a.tran();b.tran();
for(int i=0;i<c.n;++i){
c.a[i]+=a.a[i]-b.a[i];
if(c.a[i]<0) c.a[i]+=10,--c.a[i+1];
}
while(c.n>1&&!c.a[c.n-1]) --c.n;
c.tran();
return c;
}
friend Number operator + (Number a,Number b){
Number c;bool fl=false;
if(a.POrN^b.POrN){
if(!a.POrN){
swap(a,b);fl=true;
}
if(a.abs(a)<b.abs(b)){
c=c.sub(b,a);c.POrN=false;
}
else{
c=c.sub(a,b);c.POrN=true;
}
}
else{
c=c.add(a,b);c.POrN=a.POrN;
}
if(fl) swap(a,b);
return c;
}
friend Number operator - (const Number a,const Number b){
Number c=b;c.POrN=!c.POrN;return a+c;
}
friend Number operator * (Number a,Number b){
Number c;c.init();
c.n=a.n+b.n;
c.POrN=!(a.POrN^b.POrN);
a.tran();b.tran();
for(int i=0;i<a.n;++i)
for(int j=0;j<b.n;++j)
c.a[i+j]+=a.a[i]*b.a[j];
for(int i=0;i<c.n;++i)
if(c.a[i]>10){
c.a[i+1]+=c.a[i]/10;c.a[i]%=10;
}
while(c.n>1&&!c.a[c.n-1]) --c.n;
a.tran();b.tran();c.tran();
return c;
}
bool cmp(Number c,int s,int t){
if(t-s>n) return true;
if(t-s<n) return false;
for(int i=0;i<n;++i){
if(a[i]==c.a[s+i]) continue;
return c.a[s+i]>a[i];
}
return true;
}
void sub(Number b,int s,int t){
for(int i=t-1,j=b.n-1;j>=0;--i,--j){
a[i]-=b.a[j];
if(a[i]<0) a[i]+=10,--a[i-1];
}
return;
}
friend Number operator / (Number a,Number b){
Number c,d=a;c.init();
c.POrN=!(a.POrN^b.POrN);c.n=0;
for(int s=0,t=b.n;t<=d.n;){
while(b.cmp(d,s,t)){
d.sub(b,s,t);++c.a[c.n];
if(!d.a[s]) ++s;
}
++c.n;++t;
if(!d.a[s]) ++s;
}
int cnt=0;
while(cnt<c.n&&!c.a[cnt]) ++cnt;
c.n-=cnt;
for(int i=0;i<c.n;++i)
c.a[i]=c.a[i+cnt];
if(!c.n){
++c.n;c.POrN=true;
}
return c;
}
};
graph
边链表
struct graph{
int nxt,to;
}e[M];
int g[N],n,m,cnt;
inline void addedge(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline void dfs(int u){
for(int i=g[u],k;i;i=e[i].nxt)
最短路
spfa
inline void spfa(int u){
q.push(u);inq[u]=true;
while(!q.empty()){
u=q.front();q.pop();inq[u]=false;
for(int i=g[u];i;i=e[i].nxt)
if(dis[u]+e[i].w>dis[e[i].to]){
dis[e[i].to]=dis[u]+e[i].w;
if(!inq[e[i].to]){
inq[e[i].to]=true;q.push(e[i].to);
}
}
}
}
dijkstra
struct dist{
int u;ll dis;
dist(int _u,int _dis){
u=_u;dis=_dis;
}
friend bool operator < (const dist b,const dist a){
return a.dis<b.dis;
}
};
ll dis[N];
priority_queue<dist> q;
void dijkstra(int u){
for(int i=1;i<=n;++i) dis[i]=-1;
dis[u]=0;
q.push(dist(u,dis[u]));
while(!q.empty()){
dist d=q.top();u=d.u;q.pop();
if(d.dis>dis[u]) continue;
for(int i=g[u];i;i=e[i].nxt)
if(dis[e[i].to]==-1||dis[u]+e[i].w<dis[e[i].to]){
dis[e[i].to]=dis[u]+e[i].w;
q.push(dist(e[i].to,dis[e[i].to]));
}
}
}
强连通分量
tarjan
时间复杂度:\(O(n+m)\)
用途:有向图缩环
int f[N],dfn[N],low[N],sta[N],top;
/*dfn[u]:遍历到u点的时间; low[u]:u点可到达的各点中最小的dfn[v],即最高层的点*/
bool ins[N];
inline void tarjan(int u){
dfn[u]=low[u]=++cnt;
sta[++top]=u;ins[u]=true;
for(int i=g[u];i;i=e[i].nxt)
if(!dfn[e[i].to]){
tarjan(e[i].to);
low[u]=min(low[u],low[e[i].to]);
}
else if(ins[e[i].to])
low[u]=min(low[u],low[e[i].to]);
if(dfn[u]==low[u]){
while(sta[top+1]!=u){
f[sta[top]]=u;
ins[sta[top--]]=false;
}
}
}
inline void solve(){
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
}
割边
割边,又称桥.
\(dfn[\;],low[\;]\)的定义同\(tarjan\)求有向图强连通分量.
枚举当前点\(u\)的所有邻接点\(v\):
1.如果某个邻接点\(v\)未被访问过,则访问\(v\),并在回溯后更新\(low[u]=min(low[u],low[v])\);
2.如果某个邻接点\(v\)已被访问过,则更新\(low[u]=min(low[u],dfn[v])\).
对于当前节点\(u\),如果邻接点中存在一点\(v\)满足\(low[v]>dfn[u]\)(\(v\)向上无法到达\(u\)及\(u\)祖先)说明\((u,v)\)为一条割边.
inline void tarjan(int u,int fa){
dfn[u]=low[u]=++cnt;
for(int i=g[u];i;i=e[i].nxt)
if(!dfn[e[i].to]){
tarjan(e[i].to,u);
low[u]=min(low[u],low[e[i].to]);
if(low[e[i].to]>dfn[u]) cut[e[i].n]=true;
}
else if(e[i].to!=fa)
low[u]=min(low[u],dfn[e[i].to]);
}
割点
\(dfn[\;],low[\;]\)的定义同\(tarjan\)求有向图强连通分量.
枚举当前点\(u\)的所有邻接点\(v\):
1.如果某个邻接点\(v\)未被访问过,则访问\(v\),并在回溯后更新\(low[u]=min(low[u],low[v])\);
2.如果某个邻接点\(v\)已被访问过,则更新\(low[u]=min(low[u],dfn[v])\).
对于当前节点\(u\),
如果\(u\)为搜索树中的根节点,若它的子节点数\(\geq2\)(根是多棵子树上节点的唯一连通方式),则\(u\)为割点;
如果\(u\)为搜索树上的非根节点,若存在子节点\(v\)满足\(low[v]\;\geq\;dfn[u]\)(\(v\)向上无法到达\(u\)的祖先),则\(u\)为割点.
inline void tarjan(int u,int fa){
dfn[u]=low[u]=++cnt;
for(int i=g[u];i;i=e[i].nxt){
++t[u];
if(!dfn[e[i].to]){
tarjan(e[i].to,u);
low[u]=min(low[u],low[e[i].to]);
if(u==rt){
if(t[u]>=2) cut[u]=true;
}
else if(low[e[i].to]>=dfn[u]) cut[u]=true;
}
else if(e[i].to!=fa)
low[u]=min(low[u],dfn[e[i].to]);
}
}
匈牙利算法
时间复杂度:\(O(m\sqrt{n})\)
#define N 3001
#define M 200001
struct graph{
int nxt,to;
}e[M];
int g[N],fr[N],n,m,cnt;
bool u[N];
inline void addedge(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline bool match(int x){
for(int i=g[x];i;i=e[i].nxt)
if(!u[e[i].to]){
u[e[i].to]=true;
if(!fr[e[i].to]||match(fr[e[i].to])){
fr[e[i].to]=x;return true;
}
}
return false;
}
inline int hungary(){
int ret=0;
for(int i=1;i<=n;i++){
fill(u+1,u+1+n,false);
if(match(i)) ret++;
}
return ret;
}
inline void init(){
scanf("%d%d",&n,&m);
for(int i=1,j,k;i<=m;i++){
scanf("%d%d",&j,&k);
addedge(j,k);
}
printf("%d",hungary());
}
2-SAT
- 根据条件表达式建边:\((x,y)\)表示如果选了\(x\)必须选\(y\);
- 缩环;
- 判断是否可行;
- 根据缩完环的图反向建边;
- 拓扑排序进行染色(\(1\)表示\(true,2\)表示\(false\))。
时间复杂度:\(O(n+m)\)
/*假设输入是无空格无括号的的c++条件表达式且未知数用编号代替*/
#define L 11
#define N 100001
#define M 1000001
struct graph{
int nxt,to;
}e[M],gra[M];
int sol[N],col[N];
int f[N],dfn[N],low[N],sta[N];
int g[N],go[N],to[N],n,m,nn,cnt;
bool ins[N];char c[L];queue<int> q;
inline addedge(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline adde(int x,int y){
gra[++cnt].nxt=go[x];go[x]=cnt;gra[cnt].to=y;
}
inline void type(){
int l=strlen(c),i=0,x=0,y=0;
bool flag1=true,flag2=true;
if(c[i]=='!'){
flag1=false;i++;
}
while(i<l&&c[i]>='0'&&c[i]<='9')
x=x*10+c[i++]-'0';
if(i==l){
if(flag1) addedge(x+n,x);
else addedge(x,x+n);
return;
}
char cha=c[i];i++;
if(c[i]=='!'){
flag2=false;i++;
}
while(i<l&&c[i]>='0'&&c[i]<='9')
y=y*10+c[i++]-'0';
if(cha=='&'){
if(flag1&&flag2){
addedge(x+n,x);
addedge(y+n,y);
}
else if(flag1){
addedge(x+n,x);
addedge(y,y+n);
}
else if(flag2){
addedge(x,x+n);
addedge(y+n,y);
}
else{
addedge(x,x+n);
addedge(y,y+n);
}
}
else if(cha=='|'){
if(flag1&&flag2){
addedge(x+n,y);
addedge(x,y+n);
}
else if(flag1){
addedge(x+n,y+n);
addedge(y,x);
}
else if(flag2){
addedge(x,y);
addedge(y+n,x+n);
}
else{
addedge(x,y+n);
addedge(y,x+n);
}
}
else if(cha=='^'){
if(flag1&&flag2){
addedge(x,y+n);
addedge(x+n,y);
addedge(y,x+n);
addedge(y+n,x);
}
else if(flag1){
addedge(x,y);
addedge(x+n,y+n);
addedge(y,x);
addedge(y+n,x+n);
}
else if(flag2){
addedge(x,y);
addedge(x+n,y+n);
addedge(y,x);
addedge(y+n,x+n);
}
else{
addedge(x,y+n);
addedge(x+n,y);
addedge(y,x+n);
addedge(y+n,x);
}
}
}
inline void tarjan(int u){
dfn[u]=low[u]=++cnt;
sta[++sta[0]]=u;ins[u]=true;
for(int i=g[u];i;i=e[i].nxt)
if(!dfn[e[i].to]){
tarjan(e[i].to);
low[u]=min(low[u],low[e[i].to]);
}
else if(ins[e[i].to])
low[u]=min(low[u],low[e[i].to]);
if(dfn[u]==low[u]){
while(sta[sta[0]+1]!=u){
f[sta[sta[0]]]=u;
ins[sta[0]--]=false;
}
}
}
inline bool chk(){
for(int i=1;i<=n;i++)
if(f[i]==f[i+n]) return false;
return true;
}
inline void build(){
cnt=0;
for(int i=1;i<=nn;i++)
for(int j=g[i];j;j=e[j].nxt){
if(f[i]!=f[e[j].to]){
adde(f[e[j].to],f[i]);to[i]++;
}
}
}
inline void toposort(){
for(int i=1;i<=nn;i++)
if(f[i]==i&&!to[i]) q.push(i);
while(!q.empty()){
int u=q.front();q.pop();
if(!col[u]){
col[u]=1;col[u+n]=-1;
}
for(int i=go[u];i;i=gra[i].nxt)
if(!(--to[gra[i].to]))
q.push(gra[i].to);
}
}
inline void init(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%s",&c);type();
}
nn=n<<1;cnt=0;
for(int i=1;i<=nn;i++)
if(!dfn[i]) tarjan(i);
if(!chk()){
printf("No solution.\n");
return;
}
build();
toposort();
for(int i=1;i<=n;i++)
col[i]=col[f[i]];
for(int i=1;i<=n;i++)
if(col[i]>0) printf("%d:true\n",i);
else printf("%d:false\n",i);
}
网络流
最大流
- 二分图最大匹配
- KM(最大权匹配)
- 最小路径覆盖:在一个有向图中,找出最少的路径,使得这些路径经过了所有的点。z.B. 1->3>4,2,5
- 每个顶点i属于且只属于一个路径。
- 拆点
- 路径数=顶点数-匹配数
- 二分图多重匹配
inline void adde(int x,int y,int f){
addedge(x,y,f);addedge(y,x,0);
}
inline bool bfs(int u){
memset(dep,0,sizeof(dep));
q.push(u);dep[u]=1;
while(!q.empty()){
u=q.front();q.pop();
for(int i=g[u];i;i=e[i].nxt)
if(e[i].f>0&&!dep[e[i].to]){
q.push(e[i].to);
dep[e[i].to]=dep[u]+1;
}
}
return dep[t];
}
inline int dfs(int u,int f){
if(u==t) return f;
int ret=0;
for(int i=g[u],d;i&&f;i=e[i].nxt)
if(e[i].f>0&&dep[e[i].to]>dep[u]){
d=dfs(e[i].to,min(e[i].f,f));
f-=d;ret+=d;e[i].f-=d;e[i^1].f+=d;
}
if(!ret) dep[u]=-1;
return ret;
}
inline int dinic(){
int ret=0;
while(bfs(s))
ret+=dfs(s,inf);
return ret;
}
最小割=最大流
- 割:一种 点的划分方式:将所有的点划分为\(S\)和\(T=V-S\) 两个集合,其中源点属于\(S\),汇点属于\(T\)。
- 最大权闭合子图=总收益-最大流
- 最大点权独立集=总点权-最大流
费用流
- 最长不相交路径
- 最大权不相交路径
inline void adde(int x,int y,int f,int w){
addedge(x,y,f,w);addedge(y,x,0,-w);
}
inline bool spfa(int u){
for(int i=1;i<=t;++i){
dis[i]=INF;inq[i]=false;
}
q.push(u);dis[u]=0;inq[u]=true;
while(!q.empty()){
u=q.front();q.pop();inq[u]=false;
for(int i=g[u];i;i=e[i].nxt)
if(e[i].f>0&&dis[u]+e[i].w<dis[e[i].to]){
dis[e[i].to]=dis[u]+e[i].w;
pre[e[i].to].e=i;pre[e[i].to].v=u;
if(!inq[e[i].to]){
q.push(e[i].to);inq[e[i].to]=true;
}
}
}
return dis[t]<INF;
}
inline int mf(){//最小费用最大流
int ret=0,d;
while(spfa(s)){
d=INF;
for(int i=t;i!=s;i=pre[i].v)
d=min(d,e[pre[i].e].f);
ret+=d*dis[t];
for(int i=t;i!=s;i=pre[i].v){
e[pre[i].e].f-=d;
e[pre[i].e^1].f+=d;
}
}
return ret;
}
inline void adde(int x,int y,int f,int w){
addedge(x,y,f,w);addedge(y,x,0,-w);
}
inline bool spfa(int u){
for(int i=0;i<(t<<1);++i){
dis[i]=-INF;inq[i]=false;
}
q.push(u);dis[u]=0;inq[u]=true;
while(!q.empty()){
u=q.front();q.pop();inq[u]=false;
for(int i=g[u];i;i=e[i].nxt){
if(e[i].f>0&&dis[u]+e[i].w>dis[e[i].to]){
dis[e[i].to]=dis[u]+e[i].w;
pre[e[i].to].e=i;pre[e[i].to].v=u;
if(!inq[e[i].to]){
inq[e[i].to]=true;q.push(e[i].to);
}
}
}
}
return dis[t]>-INF;
}
inline int mf(){//最大费用最大流
int ret=0,d;
while(true){
if(!spfa(s)) return ret;
d=INF;
for(int i=t;i!=s;i=pre[i].v){
d=min(d,e[pre[i].e].f);
}
ret+=d*dis[t];
for(int i=t;i!=s;i=pre[i].v){
e[pre[i].e].f-=d;e[pre[i].e^1].f+=d;
}
}
}
有上下界的网络流
基本建图
建立超级源\(S\),超级汇\(T\).
对于边\((u,v)\)=\([l,u]\),将其拆成三条边:
- \((S,v)=l\);
- \((u,v)=u-l\);
- \((u,T)=l.\)
因为对于边\((u,v)=[l,u]\),
\(u\)至少流出\(l\)的流量,\(v\)至少流入\(l\)的流量,所以建边\((S,v)=l,(u,T)=l\);
而\(u->v\)有\(u-l\)的流量是自由流,所以建边\((u,v)=u-l\).
显然\(S->u,u->T\)有可能有多条边,合并这些边,节省空间.
- 无源汇
可行流
求\(S->T\)的最大流,从\(S\)出发的边全部满流则可行,因为说明所有边的下界均已满足.每条边的实际流为自由流+流量下界. - 有源汇
可行流
加一条边\((t,s)=+\infty\).转成无源汇.
求\(S->T\)的最大流,从\(S\)出发的边全部满流则可行.
最大流
求出可行流后,在残量网络上求\(s->t\)的最大流.
理由:
\(s->t\)跑的是\(S->T\)的反向边,这时下界的流量已经在反向边中了,\((t,s)=+\infty,S,T\)不会影响到最大流,所以是合法的答案.
最小流
先不加\((t,s)=+\infty\)这条边,这时跑\(S->T\)的最大流可求出\(t->s\)的最大流,也就是在合法的情况下最多能减去多少.
然后再加\((t,s)=+\infty\)这条边,此时残量网络\(S->T\)的最大流即为答案.
tree
lca
\(f[i][j]\)表示节点\(i\)的祖先中,与节点\(i\)距离为\(2^j\)的节点编号.
那么\(f[i][j]=\begin{cases}root&i=root\\ father[i]&j=0,i\not=root\\ f[i][j]=f[f[i][j-1]][j-1]&j>0,i\not=root\end{cases}\)
#define K 20
#define N 10005
#define M 100005
struct graph{
int nxt,to;
}e[M];
int f[N][K],g[N],dep[N],m,n,q,cnt;
stack<int> s;
inline void addedge(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline void dfs(int u){
dep[u]=1;s.push(u);
while(!s.empty()){
u=s.top();s.pop();
if(u!=1) for(int i=1;i<K;++i)
f[u][i]=f[f[u][i-1]][i-1];
else for(int i=0;i<K;++i)
f[u][i]=u;
for(int i=g[u];i;i=e[i].nxt){
if(!dep[e[i].to]){
dep[e[i].to]=dep[u]+1;
f[e[i].to][0]=u;
s.push(e[i].to);
}
}
}
}
inline int swim(int x,int h){
for(int i=0;h;++i,h>>=1)
if(h&1) x=f[x][i];
return x;
}
inline int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
x=swim(x,dep[x]-dep[y]);
if(x==y) return x;
int i=K-1;
while(true){
if(f[x][0]==f[y][0]) return f[x][0];
for(;f[x][i]==f[y][i];--i);
x=f[x][i];y=f[y][i];
}
}
inline void init(){
scanf("%d%d",&n,&m);
for(int i=1,j,k;i<=m;++i){
scanf("%d%d",&j,&k);
addedge(j,k);addedge(k,j);
}
scanf("%d",&q);dfs(1);
for(int i=1,j,k;i<=q;i++){
scanf("%d%d",&j,&k);
printf("%d\n",lca(j,k));
}
}
树链剖分
基本思想
一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每条边属于且只属于一条链,然后再通过数据结构来维护每一条链。
一些定义
树链:树上的路径.
剖分:把路径分类为重链和轻链.
重儿子:\(u\)的子节点中\(siz[v]\)值最大的\(v\).
轻儿子:\(u\)的其它子节点.
重边:点\(u\)与其重儿子的连边.
轻边:点\(u\)与其轻儿子的连边.
重链:由重边连成的路径.
轻链:轻边.
性质
- 如果\((u,v)\)为轻边,则\(siz[v]\;\times\;2<siz[u]\).
- 从根到某一点的路径上轻链、重链的个数都不大于\(logn\).
- 树剖序其实也可以是\(dfs\)序的一种.
实现
一些变量:
\(f[u]\)表示\(u\)的父亲.
\(siz[u]\)表示以\(u\)为根的子树的大小.
\(dep[u]\)表示\(u\)的深度(根深度为\(1\)).
\(top[u]\)表示\(u\)所在的链的顶端节点.
\(son[u]\)表示与\(u\)的重儿子.
重标号:
\(p[u]\):重标号后\(u\)的编号.
\(dfs\)序:\(dfs\)的时候先走重边.
这样可以使得重边的编号是连续的,方便维护.
用两遍\(dfs\)求出所需的所有变量以及重标号.
预处理
int f[N],p[N],dep[N],siz[N],son[N],top[N];
/*top[u]:u所在的链的顶端节点,son[u]:u的重儿子*/
inline void dfs1(int u){
int m=0;siz[u]=1;
for(int i=g[u];i;i=e[i].nxt)
if(!dep[e[i].to]){
f[e[i].to]=u;
dep[e[i].to]=dep[u]+1;
dfs1(e[i].to);
siz[u]+=siz[e[i].to];
if(siz[e[i].to]>m){
son[u]=e[i].to;
m=siz[e[i].to];
}
}
}
inline void dfs2(int u,int tp){
top[u]=tp;p[u]=++cnt;ww[cnt]=w[u];
if(son[u]) dfs2(son[u],tp);
for(int i=g[u];i;i=e[i].nxt){
if(e[i].to!=f[u]&&e[i].to!=son[u])
dfs2(e[i].to,e[i].to);
}
}
访问修改(u,v):
类似倍增的走法,每次将深度大的往上移,直到\(u,v\)属于同一条链.
inline int sum(int x,int y){
int ret=0,t;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]){
t=x;x=y;y=t;
}
ret+=ask(1,p[top[x]],p[x]);
x=f[top[x]];
}
if(p[x]>p[y]){
t=x;x=y;y=t;
}
ret+=ask(1,p[x],p[y]);
return ret;
}
inline void change(int x,int y,int k){
int t;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]){
t=x;x=y;y=t;
}
cover(1,p[top[x]],p[x],k);
x=f[top[x]];
}
if(p[x]>p[y]){
t=x;x=y;y=t;
}
cover(1,p[x],p[y],k);
}
数据结构
并查集
inline int gf(int k){
if(f[k]==k) return k;
int fa=gf(f[k]);
return f[k]=fa;
}
树状数组
inline int lowbit(int x){
return x&(-x);
}
inline void add(int x,int k){
for(int i=x;i<=n;i+=lowbit(i))
s[i]+=k;
}
inline int ask(int x){
int ret=0;
for(int i=x;i;i-=lowbit(i))
ret+=s[i];
return ret;
}
线段树
struct SegMent{
int l,r;ll sum/*ans*/,lzy;
}lt[M];
inline void build(int u,int l,int r){
lt[u].l=l;lt[u].r=r;
if(lt[u].l<lt[u].r){
int lef=u<<1,rig=u<<1|1;
int mid=(lt[u].l+lt[u].r)>>1;
build(lef,l,mid);build(rig,mid+1,r);
}
else{
int p=w[lt[u].l];
lt[u].sum=ans[p];
}
}
inline void pushdown(int u){
if(!lt[u].lzy&&!lt[u].lzt) return;
if(lt[u].l<lt[u].r){
int lef=u<<1,rig=u<<1|1;ll s;
s=lt[u].lzy;lt[lef].lzy+=s;lt[rig].lzy+=s;
s=lt[u].lzt;lt[lef].lzt+=s;lt[rig].lzt+=s;
}
else{
ll s=lt[u].lzy;int p=w[lt[u].l];
lt[u].sum+=1ll*(siz[p]-siz[son[p]])*s;
s=lt[u].lzt;lt[u].sum+=1ll*(tot[p]<<1ll)*s;
}
lt[u].lzy=lt[u].lzt=0;
}
inline void add(int u,int x,ll s){
pushdown(u);
if(lt[u].l==lt[u].r){
lt[u].sum+=s;return;
}
if(lt[u].l<lt[u].r){
int lef=u<<1,rig=u<<1|1;
int mid=(lt[u].l+lt[u].r)>>1;
if(x<=mid) add(lef,x,s);
else add(rig,x,s);
}
}
inline ll ask(int u,int x){
pushdown(u);
if(lt[u].l==lt[u].r)
return lt[u].sum;
if(lt[u].l<lt[u].r){
int lef=u<<1,rig=u<<1|1;
int mid=(lt[u].l+lt[u].r)>>1;
if(x<=mid) return ask(lef,x);
return ask(rig,x);
}
}
堆
inline void swim(int i){
heap a=h[i];int j=i>>1;
while(j&&chk(a,h[j])){
h[i]=h[j];num[h[i].n]=i;i=j;j>>=1;
}
h[i]=a;num[h[i].n]=i;
}
inline void sink(int i){
heap a=h[i];int j=i<<1;
while(j<=cnt){
if(j<cnt&&chk(h[j+1],h[j])) ++j;
if(chk(a,h[j])) break;
h[i]=h[j];num[h[i].n]=i;i=j;j<<=1;
}
h[i]=a;num[h[i].n]=i;
}
可并堆
可以支持合并的堆.
/*大根堆*/
struct heap{
int l,r,w,d;
}h[N];
int rt[N];//第i个堆的根的下标
/*合并以x,y为根的堆*/
inline int merge(int x,int y){
//其中一个堆为空
if(!x||!y) return x+y;
//使得x,y两个根中x大
if(h[x].w<h[y].w) swap(x,y);
//保持堆两边的平衡
h[x].r=merge(y,h[x].r);
if(h[h[x].l].d<h[h[x].r].d)
swap(h[x].l,h[x].r);
h[x].d=h[h[x].r].d+1;
return x;
}
inline int pop(int x){
int l=h[x].l,r=h[x].r;
h[x].l=h[x].r=h[x].w=0;
return merge(l,r);
}
inline int top(x){
return h[x].w;
}
splay
时间复杂度:\(O(nlogn)\).
#define N 100005
struct Splay{
int c[2],f,siz,val,cnt;//balabala...(根据题目需要的变量)
}tr[N];
int n,rt,cnt;
inline bool son(int u){
return tr[tr[u].f].c[1]==u;
}
inline void ins_p(int f,int u,int c){
tr[f].c[c]=u;tr[u].f=f;
}
inline void recnt(int u){
tr[u].siz=tr[tr[u].c[0]].siz+tr[tr[u].c[1]].siz+tr[u].cnt;
}
inline void rotate(int u){
int f=tr[u].f;bool c=son(u);
if(tr[f].f) ins_p(tr[f].f,u,son(f));
else tr[u].f=0,rt=u;
ins_p(f,tr[u].c[c^1],c);
ins_p(u,f,c^1);
recnt(f);recnt(u);
}
inline void splay(int u,int rt){
while(tr[u].f!=rt){
if(tr[tr[u].f].f==rt) rotate(u);
else if(son(tr[u].f)==son(u)){
rotate(tr[u].f);rotate(u);
}
else{
rotate(u);rotate(u);
}
}
}
inline int kth(int k)/*第k小的值*/{
int u=rt;
while(u){
if(k<=tr[tr[u].c[0]].siz)
u=tr[u].c[0];
else{
k-=tr[tr[u].c[0]].siz;
if(k<=tr[u].cnt) return tr[u].val;
k-=tr[u].cnt;u=tr[u].c[1];
}
}
return u;
}
inline int near(int u,bool c){
if(tr[u].c[c]){
u=tr[u].c[c];c^=1;
while(tr[u].c[c])
u=tr[u].c[c];
return u;
}
while(u&&son(u)==c) u=tr[u].f;
return tr[u].f;
}
inline int select(int u,int v){
u=near(u,0);v=near(v,1);
splay(u,0);splay(v,rt);
return tr[v].c[0];
}
inline void clear(int u){
tr[tr[u].f].c[son(u)]=0;
tr[u].c[0]=tr[u].c[1]=tr[u].f=0;
tr[u].siz=tr[u].val=tr[u].cnt=0;
}
inline void del(int u,int v){
u=select(u,v);
int f=tr[u].f;clear(u);u=f;
while(u){
recnt(u);u=tr[u].f;
}
}
inline int find(int k){
int u=rt;
while(u&&tr[u].val!=k)
u=tr[u].c[k>tr[u].val];
return u;
}
inline void insert(int k){
int u=find(k);
if(u){
++tr[u].cnt;
while(u){
recnt(u);u=tr[u].f;
}
return;
}
u=rt;
while(tr[u].c[k>tr[u].val])
u=tr[u].c[k>tr[u].val];
tr[++cnt].val=k;
tr[cnt].siz=tr[cnt].cnt=1;
if(!u){
rt=cnt;recnt(rt);return;
}
ins_p(u,cnt,k>tr[u].val);
splay(cnt,0);
}
LCT
时间复杂度:\(O(nlogn)\).
#define N 100005
struct LCT{
int c[2],f,rev;
}tr[N];
int n;
stack<int> s;
inline void down(int u){
if(tr[u].rev){
tr[u].rev=0;
tr[tr[u].c[0]].rev^=1;
tr[tr[u].c[1]].rev^=1;
swap(tr[u].c[0],tr[u].c[1]);
}
}
inline bool son(int u){
return tr[tr[u].f].c[1]==u;
}
inline void ins_p(int f,int u,int c){
tr[f].c[c]=u;tr[u].f=f;
}
inline bool isroot(int u){
return tr[tr[u].f].c[0]!=u&&tr[tr[u].f].c[1]!=u;
}
inline void recnt(int u){
//balabala...
}
inline void rotate(int u){
int f=tr[u].f;bool c=son(u);
if(isroot(f)) tr[u].f=tr[f].f;
else ins_p(tr[f].f,u,son(f));
ins_p(f,tr[u].c[c^1],c);
ins_p(u,f,c^1);
recnt(f);recnt(u);
}
inline void splay(int u){
s.push(u);
for(int v=u;!isroot(v);v=tr[v].f) s.push(tr[v].f);
while(!s.empty()){
down(s.top());s.pop();
}
while(!isroot(u)){
if(isroot(tr[u].f)) rotate(u);
else if(son(tr[u].f)==son(u)){
rotate(tr[u].f);rotate(u);
}
else{
rotate(u);rotate(u);
}
}
}
inline void access(int u){
for(int lst=0;u;lst=u,u=tr[u].f){
splay(u);tr[u].c[1]=lst;recnt(u);
}
}
inline void makeroot(int u){
access(u);splay(u);tr[u].rev^=1;
}
inline int findroot(int u){
access(u);splay(u);
while(down(u),tr[u].c[0]) u=tr[u].c[0];
splay(u);
return u;
}
inline void select(int u,int v){
makeroot(u);access(v);splay(v);//u为v的左孩子
}
inline void link(int u,int v){
makeroot(u);tr[u].f=v;
}
inline void cut(int u,int v){
select(u,v);tr[v].c[0]=tr[u].f=0;recnt(v);
}
Dsu on tree
主要思想:暴力,最大子树只跑一遍。
时间复杂度:\(O(nlogn)\).
//求每个子树的众数和
ll sum[maxn],buk[maxn],mx;
inline void up(int color){
if(buk[color]) sum[buk[color]] -= color;
buk[color] ++ ;
if(buk[color]) sum[buk[color]] += color;
if(buk[color]>mx) mx=buk[color];
}
inline void down(int color){
if(buk[color]==0) return;
sum[buk[color]] -= color;
if( buk[color]==mx && sum[mx]==0 ) mx--;
buk[color] -- ; sum[buk[color]] += color;
}
int size[maxn],son[maxn],dfs[maxn],l[maxn],r[maxn],topdfs;
void findheavy(int x,int fa){
dfs[++topdfs] = x; l[x]=topdfs;
int mxsize=0; size[x]=1;
for(int i=head[x];i;i=nxt[i]){
if(to[i]==fa) continue;
findheavy(to[i],x);
size[x] += size[to[i]];
if(size[to[i]]>mxsize){
son[x]=to[i];
mxsize=size[to[i]];
}
}
r[x]=topdfs;
}
ll ans[maxn];
void dsu(int x,int fa,bool keep){
for(int i=head[x];i;i=nxt[i]){
if(to[i]!=fa&&to[i]!=son[x])
dsu(to[i],x,0);
}
if(son[x]) dsu(son[x],x,1);
for(int i=head[x];i;i=nxt[i]){
if(to[i]!=fa&&to[i]!=son[x])
for(int j=l[to[i]];j<=r[to[i]];j++)
up(c[dfs[j]]);
}
up(c[x]);
ans[x]=sum[mx];
if(keep==0){
for(int j=l[x];j<=r[x];j++)
down(c[dfs[j]]);
}
}
ST表
给定一个数列\(a,O(nlogn)\)预处理,\(O(1)\)查询数列在区间\([l,r]\)的最值.
本文介绍求最大值.
预处理
\(st[i][j]\)表示\(max\{a_k\}(k\in[i,i+2^j))\).
\(st[i][j]=\begin{cases}a_i&j=0\\max(st[i][j-1],st[i+2^{j-1}][j-1])&j>0\\\end{cases}\)
询问
询问\([l,r]\),令\(k=\lfloor\;log_2^{r-l+1}\;\rfloor\),则答案为\(max(st[l][k],st[r-2^k+1][k])\).
int st[N][K],a[N],log_2[N];
inline void ini_st(){
log_2[1]=0;
for(int i=2;i<=n;++i){
log_2[i]=log_2[i-1];
if((1<<log_2[i]+1)==i)
++log_2[i];
}
for(int i=n;i;--i){
st[i][0]=a[i];
for(int j=1;(i+(1<<j)-1)<=n;++j)
st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
}
inline int ask(int l,int r){
int k=log_2[r-l+1];
return max(st[l][k],st[r-(1<<k)+1][k]);
}
计算几何
struct point{
int x,y;
};
point operator - (point a,point b){
return (point){a.x-b.x,a.y-b.y};
}
ll operator * (point a,point b){
return 1ll*a.x*b.y-1ll*b.x*a.y;
}
凸包
时间复杂度:\(O(nlogn)\).
#define N 100005
#define eps 1e-13
bool operator < (point a,point b){
if(a.x!=b.x) return a.x<b.x;
return a.y<b.y;
}
inline double sqr(int k){
return (double)(k*k);
}
inline double dis(point x,point y){
return sqr(x.x-y.x)+sqr(x.y-y.y);
}
inline bool cmp(point x,point y){
if(fabs(x.an-y.an)<eps)
return dis(x,a[1])>dis(y,a[1]);
return x.an<y.an;
}
inline void convex(point a[],int &n){
for(int i=2;i<=n;++i)
if(a[i]<a[1]) swap(a[i],a[1]);
for(int i=2;i<=n;++i)
a[i].an=atan2(a[i].y-a[1].y,a[i].x-a[1].x);
sort(a+2,a+1+n,cmp);
int m=1;a[++n]=a[1];
for(int i=2;i<=n;++i){
if(fabs(a[i].an-a[i-1].an)<eps) continue;
while(m>1&&(a[i]-a[m-1])*(a[m]-a[m-1])>0) --m;
a[++m]=a[i];
}
n=m;
}
旋转卡壳
时间复杂度:\(O(n)\).
#define N 100005
point a[N];int n;
inline double sqr(int k){
return (double)(k*k);
}
inline double dis(point x){
return sqrt(sqr(x.x)+sqr(x.y));
}
inline int Nxt(int k){
return (++k>n)?1:k;
}
inline double rotate(){
point p;double di,dia=0.0;
if(n==1) return dia;
for(int i=1,j=2;i<=n;++i){
p=a[Nxt(i)]-a[i];
while(abs(p*(a[j]-a[i]))<abs(p*(a[Nxt(j)]-a[i]))) j=Nxt(j);
dia=max(dia,max(dis(a[i]-a[j]),dis(a[Nxt(i)]-a[Nxt(j)])));
}
return dia;
}
转角法
时间复杂度:\(O(n)\).
point a[N];int n;
inline int cmp(ll x){
return x?(x>0?1:-1):0;
}
inline bool onseg(point p,point a,point b){
if(cmp((a-p)*(b-p))) return false;
return cmp(a.x-p.x)*cmp(b.x-p.x)<=0&&cmp(a.y-p.y)*cmp(b.y-p.y)<=0;
}
inline int chk(point p){//-1:轮廓上 1:多边形内 0:多边形外
int cnt=0,d1,d2,k;
for(int i=1;i<=n;++i){
if(onseg(p,a[i],a[i+1]) return -1;
k=cmp((a[i+1]-a[i])*(p-a[i]));
d1=cmp(a[i].y-p.y);d2=cmp(a[i+1].y-p.y);
if(k>0&&d1<=0&&d2>0) ++cnt;
if(k<0&&d2<=0&&d1>0) --cnt;
}
return cnt?1:0;
}
平面最近点对
时间复杂度:\(O(nlogn)\).
point a[N];int n;
bool cmpx(point a,point b){
if(a.x!=b.x) return a.x<b.x;
return a.y<b.y;
}
bool cmpy(point a,point b){
if(a.y!=b.y) return a.y<b.y;
return a.x<b.x;
}
inline double sqr(int k){
return (double)(k*k);
}
inline double dis(point a,point b){
return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
inline double md(int l,int r){
double ret=INF;
if(r-l<=20){
for(int i=l;i<r;++i)
for(int j=i+1;j<=r;++j)
ret=min(ret,dis(a[i],a[j]));
return ret;
}
int mid=(l+r)>>1;
ret=min(md(l,mid),md(mid+1,r));
while(a[l].x+ret<a[mid].x) ++l;
while(a[r].x-ret>a[mid].x) --r;
sort(a+l,a+r+1,cmpy);
for(int i=l;i<=r;++i)
for(int j=min(i+6,r);j>i;--j)
ret=min(ret,dis(a[i],a[j]));
sort(a+l,a+r+1,cmpx);
return ret;
}
inline double middis(){
sort(a+1,a+1+n,cmpx);
return md(1,n);
}
半平面交
\(f(x)=Ax+By+C.\\Ax+By+C\geq{0}:(-1,f(-1))->(1,f(1));\\Ax+By+C\leq{0}:(1,f(1))->(-1,f(-1)).\)
struct line{
point s,t;double an;
}a[N],q[N];
int h,t,n;
inline bool cmp(line a,line b){
if(fabs(a.an-b.an)>eps) return a.an<y.an;
return (a.t-a.s)*(b.s-a.s)>0;
}
inline point inter(line a,line b){
double s1,s2,tmp;point ret;
s1=(b.t-a.s)*(a.t-a.s);
s2(a.t-a.s)*(b.s-a.s);
tmp=s2/(s1+s2);
ret.x=b.s.x+(b.t.x-b.s.x)*tmp;
ret.y=b.s.y+(b.t.y-b.s.y)*tmp;
return ret;
}
inline bool chk(point p,line l){
return (p-l.s)*(l.t-l.s)>0;
}
inline void hpi(){
int m=1;
for(int i=1;i<=n;++i)
a[i].an=atan2(a[i].t.y-a[i].s.y,a[i].t.x-a[i].s.x);
sort(a+1,a+1+n,cmp);
for(int i=2;i<=n;++i)
if(fabs(a[i].an-a[i-1].an)>eps) a[++m]=a[i];
h=1;t=0;
for(int i=1;i<=m;++i){
while(h<t&&chk(inter(q[t],q[t-1]),a[i]) --t;
while(h<t&&chk(inter(q[h],q[h+1]),a[i]) ++h;
q[++t]=a[i];
}
while(h<t&&chk(inter(q[t],q[t-1]),q[h]) --t;
while(h<t&&chk(inter(q[h],q[h+1]),q[t]) ++h;
return t-h+1>=3;
}
数论
FFT
时间复杂度:\(O(nlogn)\).
#define N 100005
typedef long long ld;
const ld pi=acos(-1.0);
struct cp{
ld x,y;
cp() {}
cp(ld x,ld y):x(x),y(y) {}
friend cp operator + (cp a,cp b){
return cp(a.x+b.x,a.y+b.y);
}
friend cp operator - (cp a,cp b){
return cp(a.x-b.x,a.y-b.y);
}
friend cp operator * (cp a,cp b){
return cp(a.x*b.x-a.y*b.y,a.y*b.x+a.x*b.y);
}
}a[N],b[N],c[N];
namespace FFT{
const int F=1e5+10;
cp w[2][F];int re[F],n;
inline void init(int k){
k<<=1;n=1;while(n<k) n<<=1;
for(int i=0;i<n;++i){
w[0][i]=cp(cos(pi*2/n*i),sin(pi*2/n*i));
w[1][i]=cp(w[0][i].x,-w[0][i].y);
}
k=0;while((1<<k)<n) ++k;
for(int i=0,t;i<n;++i){
t=0;
for(int j=0;j<k;++j)
if(i&(1<<j)) t|=(1<<k-j-1);
re[i]=t;
}
}
inline void DFT(cp *a,int ty){
cp *o=w[ty];
for(int i=0;i<n;++i)
if(i<re[i]) swap(a[i],a[re[i]]);
cp tmp;
for(int l=2,m;l<=n;l<<=1){
m=l>>1;
for(cp *p=a;p!=a+n;p+=l){
for(int i=0;i<m;++i){
tmp=o[n/l*i]*p[i+m];
p[i+m]=p[i]-tmp;p[i]=p[i]+tmp;
}
}
}
if(ty) for(int i=0;i<n;++i)
a[i].x/=(ld)(n),a[i].y/=(ld)(n);
}
}
inline void Aireen(){
FFT::DFT(a,0);FFT::DFT(b,0);
for(int i=0;i<FFT::n;++i)
c[i]=a[i]*b[i];
FFT::DFT(c,1);
}
乘法逆元
扩展欧几里得求逆元
因为\(ab\equiv1(mod\;p)\),所以设\(q\)满足\(ab+pq=1\),
则可以用扩展欧几里得求关于\(b,q\)的方程\(ab+pq=1\)的一组解,即求出\(b\).
inline int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1;y=0;return a;
}
int ret=exgcd(b,a%b,y,x);
y-=a/b*x;return ret;
}
inline int inver{
r=exgcd(a,p,b,q);
if(r!=1) return -1;
return b;
}
费马小定理求逆元
因为\(a^{p-1}\equiv1(mod\;p)(gcd(a,p)=1)\),所以\(a\;\times\;a^{p-2}\equiv1(mod\;p)\),
则\(a^{p-2}\)为\(a\)在\(mod\;p\)意义下的逆元.
typedef long long ll;
inline ll power(ll x,ll k){
ll ret=1;
while(k){
if(k&1) ret=ret*x%p;
x=x*x%p;k>>=1;
}
return ret;
}
inline ll inver{
return power(a,p-2);
}
线性求逆元
\(re[i]\)表示\(i\)在\(mod\;p\)(\(p\)为质数)意义下的逆元。
\(re[i]=\begin{cases}1&i=1\\-p/i\;\times\;re[p\;mod\;i]&i>1\\\end{cases}\)
证明:
因为\((p/i)\;\times\;i+p\;mod\;i=p\),
所以\((p/i)\;\times\;i+p\;mod\;i\equiv0(mod\;p)\)
所以\((p/i)\;\times\;i\equiv-p\;mod\;i(mod\;p)\)
所以\(-p/i\;\equiv\;p\;mod\;i\times\;i^{-1}(mod\;p)\)
所以,
\(\begin{split} &-(p/i)\;\times\;re[p\;mod\;i]\\ \equiv&-(p/i)\times(p\;mod\;i)^{-1}\\ \equiv&(p\;mod\;i)\;\times\;i^{-1}\times(p\;mod\;i)^{-1}\\ \equiv&\;i^{-1}(mod\;p)\\ \end{split}\)
typedef long long ll;
inline ll inver{
re[1]=1;
for(ll i=2,mul;i<=a;++i){
re[i]=-p/i*re[p%i]%p;
if(re[i]<0){
mul=(0-re[i])/p+1;
re[i]=(re[i]+mul*p)%p;
}
}
return re[a];
}
线性筛
积性函数(素数)
若\((a,b)=1\),则\(f(ab)=f(a)\;\times\;f(b)\).
欧拉筛法保证每个合数只会被其最小的素数筛掉,所以复杂度是线性的。
int p[N],n,cnt;
bool b[N];
inline void prime(){
b[0]=b[1]=true;
for(int i=2;i<=n;++i){
if(!b[i]) p[++cnt]=i;
for(int j=1;j<=cnt&&i*p[j]<=n;++j){
b[i*p[j]]=true;
if(!(i%p[j])) break;
}
}
}
每次\(p[j]|i\)时跳出循环能保证每个合数只会被其最小的素数筛掉,因为\(i\;\times\;p[k](k>j)\)的最小素数为\(p[j]\)。
欧拉函数
欧拉函数\(\phi(x)\)的定义:小于等于\(x\)的正整数中与\(x\)互质的数的个数。
\(\phi(x)=\begin{cases}1&x=1\\x-1&x\;is\;prime\\x\prod_{i=1}^{k}(\frac{p_{i}-1}{p_{i}})&x=p_1^{a_1}\times{p_2^{a_2}}\times\dots\times{p_k^{a_k}}\\\end{cases}\)
证明:
如果\(n\)为某一素数\(p\),则\(\phi(p)=p-1\).
如果\(n\)为某一素数\(p\)的幂次\(p^a,\phi(p^a)=(p-1)\;\times\;p^{a-1}\).
欧拉函数是积性函数,即当\((a,b)=1\)时\(f(ab)=f(a)\;\times\;f(b)\).
若\(x=p_1^{a_1}\;\times\;p_2^{a_2}\;\times\dots\times\;p_k^{a_k}\),则\(\phi(x)=\prod_{i=1}^{k}(p_i-1)\;\times\;p_i^{a_i-1}=x\prod_{i=1}^{k}\frac{p_i-1}{p_i}\).
设\(p\)为\(x\)最小的质数,\(x'=x/p\),在线性筛中,\(x\)被筛\(p\;\times\;x'\)掉。
当\(x'\;mod\;p\not=0\)时,\(\phi(x)=p\;\times\;x'\times\;(\frac{p-1}{p})\prod_{i=1}^{k'}(\frac{p_i-1}{p_i})=(p-1)\;\;\times\;\phi(x')\);
当\(x'\;mod\;p=0\)时,\(\phi(x)=p\;\times\;x'\;\times\;\prod_{i=1}^{k'}(\frac{p_i-1}{p_i})=p\;\times\;\phi(x')\).
int p[N],phi[N],n,cnt;
bool b[N];
inline void prime(){
b[0]=b[1]=true;phi[1]=1;
for(int i=2;i<=n;++i){
if(!b[i]){
p[++cnt]=i;phi[i]=i-1;
}
for(int j=1;j<=cnt&&i*p[j]<=n;++j){
b[i*p[j]]=true;
if(!(i%p[j])){
phi[i*p[j]]=p[j]*phi[i];break;
}
phi[i*p[j]]=(p[j]-1)*phi[i];
}
}
}
莫比乌斯函数
莫比乌斯函数\(\mu(x)\)的定义:
\(\mu(x)=\begin{cases}1&x=1\\(-1)^{k}&x=p_{1}^{a_{1}}p_{2}^{a_{2}}\;\dots\;p_{k}^{a_{k}}(a_{i}=1)\\0&x=p_{1}^{a_{1}}p_{2}^{a_{2}}\;\dots\;p_{k}^{a_{k}}(max\{a_{i}\}>1)\end{cases}\)
显然当\(x\)是质数时,\(\mu(x)=-1\);
当\(x\)不是质数时,设\(p\)为\(x\)最小的质数,\(x'=x/p\),在线性筛中,\(x\)被筛\(p\;\times\;x'\)掉。
当\(x'\;mod\;p\not=0\)时,当\(\mu(x')\not=0\)时,显然\(a_{i}=1,\mu(x)=-\mu(x')\);
\(\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\)当\(\mu(x')=0\)时,\(\mu(x)=0\),即\(\mu(x)=-\mu(x')\);
当\(x'\;mod\;p=0\)时,显然\(max\{a_{i}\}>1\),\(\mu(x)=0\)。
int p[N],mu[N],n,cnt;
bool b[N];
inline void prime(){
b[0]=b[1]=true;mu[1]=1;
for(int i=2;i<=n;++i){
if(!b[i]){
p[++cnt]=i;mu[i]=-1;
}
for(int j=1;j<=cnt&&i*p[j]<=n;++j){
b[i*p[j]]=true;
if(!(i%p[j])){
mu[i*p[j]]=0;break;
}
mu[i*p[j]]=-mu[i];
}
}
}
高斯消元
时间复杂度:\(O(n^3)\).
#define N 101
#define eps 1e-13
double a[N][N],ans[N];
int n;bool b[N];
inline void gauss(int n){
int m=0;double tmp;
memset(b,0,sizeof(b));
for(int i=0;i<n;++i){
for(int j=m;j<n;++j){
if(fabs(a[j][i])>eps){
for(int k=i;k<=n;++k)
swap(a[m][k],a[j][k]);
break;
}
}
if(fabs(a[m][i])<eps) continue;
for(int j=0;j<n;++j)
if(j!=m&&fabs(a[j][i])>eps){
tmp=a[j][i]/a[m][i];
for(int k=i;k<=n;++k)
a[j][k]-=tmp*a[m][k];
}
b[i]=true;++m;
}
for(int i=0;i<n;++i)
if(b[i]) for(int j=0;j<n;++j)
if(fabs(a[j][i])>eps){
ans[i]=a[j][n]/a[j][i];break;
}
}
扩展欧几里得
求二元一次不定方程\(ax+by=gcd(a,b)\)的一组解。
当\(b=0\)时,有一组解\(x=1,y=0\);
当\(b>0\)时,因为\(gcd(a,b)=gcd(b,a\;mod\;b)\),
所以设\(x',y'\)满足\(bx'+(a\;mod\;b)y'=gcd(a,b)\),
则\(bx'+(a-a/b\;\times\;b)y'=gcd(a,b)\),
整理得\(ay'+b(x'-a/b\;\times\;y')=gcd(a,b)\)。
所以\(x=y',y=x'-a/b\;\times\;y'\)。
就可以在求\(gcd\)的过程中得到一组解。
inline int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1;y=0;return a;
}
else{
int ret=exgcd(b,a%b,y,x);
y-=a/b*x;return ret;
}
}
BSGS
求最小的\(x\)使得\(a^{x}\;\equiv\;b(mod\;p),p\)为质数。
令\(s=\sqrt{p}\),则\(x=y\;\times\;s+r\;(0\;\leq\;r<s)\),即\(a^{x}=a^{y\;\times\;s}\;\times\;a^{r}\)。
将所有的\(a^{r}\)用\(map\)存起来,从小到大枚举\(y\),直到\(b/a^{y\;\times\;s}\)是某个\(a^{r}\),此时答案为\(y\;\times\;s+r\)。
typedef long long ll;
map<ll,ll> m;
inline ll po(ll x,ll k,ll p){
ll ret=1;
while(k){
if(k&1) ret=ret*x%p;
x=x*x%p;k>>=1;
}
return ret;
}
inline ll bsgs(ll a,ll b,ll p){
if(gcd(a,p)!=1)
if(b) return -1;
else return 1;
ll s=sqrt(p),k=1,x=1,y;
while(s*s<=p) ++s;
for(ll i=0;i<s;++i){
if(!m[k]) m[k]=i;
k=k*a%p;
}
for(ll i=0;i<s;++i){
y=b*po(x,p-2,p)%p;
if(m.count(y))
return i*s+m[y];
x=x*k%p;
}
return -1;
}
中国剩余定理
求方程组\(x\;\equiv\;a_i(mod\;m_i)(i\in[1,n])\)的解\(x\),其中\(m_i\)两两互质.
一般版本
令\(M_i=\prod_{j\not=i}m_j\),则\((M_i,m_i)=1\),所以存在\(M_ix_i+m_iy_i=1\).
(\(x_i\)为\(M_i\)在模\(m_i\)意义下的逆元)
令\(e_i=M_ix_i\),则\(e_i\equiv\begin{cases}0(mod\;m_j)&j\not=i\\1(mod\;m_j)&j=i\\\end{cases}\)
所以\(\sum_{i=1}^{n}e_ia_i\)是方程的一个解.
在\([0,\prod_{i=1}^{n}m_i)\)中只有唯一解,所以将求出的解对\(\prod_{i=1}^{n}m_i\)取模即可.
闫神
记\(q_{i,j}\)表示\(m_i\)在模\(m_j\)意义下的逆元.
\(x\equiv\sum_{i=1}^{n}(a_i\;\times\;\prod_{j=1}^{n}(m_j\;\times\;q_{j,i}))(mod\;\prod_{i=1}^{n}m_i)\)
不互质
若要合并\(x\;\equiv\;a_i(mod\;m_i),x\;\equiv\;a_j(mod\;m_j)\),
设\(x=k_im_i+a_i=k_jm_j+a_j\),则\(k_im_i-k_jm_j=a_j-a_i\).
用\(exgcd\)求出\(k_i\)(注意无解的情况),把两个方程合并为\(x\;\equiv\;k_im_i+a_i(mod\;lcm(m_i,m_j))\).
欧拉定理
若\(a,p\;\in\;N^{+},(a,p)=1\),则\(a^{\phi(p)}\;\equiv\;1(mod\;p)\).
阶
使得\(a^x\;\equiv\;1(mod\;p)\)的最小正整数\(x\)称为\(a\)模\(p\)的阶,记为\(ord_pa\).
实现
找一个数的阶可以暴力求解,原根为\(\phi(p)\)的因数.
原根
原根:\(ord_pa=\phi(p)\)时,称\(a\)是\(p\)的原根.
\(a^1,a^2,...,a^{\phi(p)}\)在模p意义下互不相同.
如果\(p\)有原根,那么原根个数为\(\phi(phi(p))\).
lucas定理
求\(c_n^m\;mod\;p\).
将\(n,m\)写成\(p\)进制:\(n=a_0p^0\;\times\;a_1p^1\;\times\;...\;\times\;a_kp^k,m=b_0p^0\;\times\;b_1p^1\;\times\;...\;\times\;b_kp^k.\)
则\(C_{n}^{m}\;\equiv\;\prod\;C_{a_i}^{b_i}(mod\;p)\).
所以\(C_n^m\;\equiv\;C_{n\;mod\;p}^{m\;mod\;p}\;\times\;C_{n/p}^{m/p}\)
斐波那契数列
\(fib(n+m)=fib(n+1)\;\times\;fib(m)+fib(n)\;\times\;fib(m-1).\)
\(gcd(fib(i),fib(j))=fib(gcd(i,j))\).
字符串算法
字符串匹配
KMP
时间复杂度:\(O(n)\)
#define N 1000005
int nxt[N],m,n;
char a[N],b[N];
inline void get_nxt(){
for(int i=2,j=0;i<=m;++i){
while(j&&b[i]!=b[j+1]) j=nxt[j];
if(b[i]==b[j+1]) ++j;
nxt[i]=j;
}
}
inline void kmp(){
for(int i=1,j=0;i<=n;++i){
while(j&&a[i]!=b[j+1]) j=nxt[j];
if(a[i]==b[j+1]) ++j;
if(j==m) printf("%d\n",i-m+1);
}
}
扩展KMP
求一个串对于另一个串的每个后缀的\(LCP\)(最长公共前缀).
实现
类似\(KMP\)的思想处理\(a,b\)串.
先求出\(b\)串与自己的每个后缀的\(LCP\),再用类似的方法求出\(b\)串与\(a\)串的每个后缀的\(LCP\).
设当前处理到\(i\),已经处理出\(g[1...i-1]\),\(k\)满足\(k+g[k]-1\)最大,即被匹配到的范围最大.
因为\(b[1...g[k]]=b[k...k+g[k]-1]\),所以\(b[i-k+1...g[k]]=b[i...k+g[k]-1]\).
如果\(i+g[i-k+1]-1<k+g[k]+1\),那么\(g[i]=g[i-k+1]\),
否则暴力匹配\(b[k+g[k]...n]和b[k+g[k]-i+1...n]\).
#define N 1000005
#define M 1000005
int f[N],g[M],n,m;
char a[N],b[M];
void exkmp(){
for(int i=1;i<m;++i)
if(b[i]!=b[i+1]){
g[2]=i-1;break;
}
if(!g[2]) g[2]=n-1;
for(int i=3,j,k=2,l;i<=m;++i){
l=k+g[k]-1;
if(g[i-k+1]<l-i)
g[i]=g[i-k+1];
else{
for(j=max(1,l-i+2;j+i-1<=m;++j)
if(b[j]!=b[j+i-1]) break;
g[i]=j-1;k=i;
}
}
for(int i=1;i<=n&&i<=m;++i)
if(a[i]!=b[i]){
f[1]=i-1;break;
}
if(!f[1]) f[1]=min(n,m);
for(int i=2,j,k=1,l;i<=n;++i){
l=k+f[k]-1;
if(g[i-k+1]<l-i)
f[i]=g[i-k+1];
else{
for(j=max(1,l-i+2;i+j-1<=m&&i+j-1<=n;++j)
if(b[j]!=a[j+i-1]) break;
f[i]=j-1;k=i;
}
}
}
trie树
时间复杂度:\(O(\sum|S_i|)\)
#define N 100005
struct trie{
int chl[26];bool b;
}tr[L];
int cnt;
inline void insert(char s[]){
int u=0,l=strlen(s+1);
for(int i=1;i<=l;++i){
if(!tr[u].chl[s[i]-'a'])
tr[u].chl[s[i]-'a']=++cnt;
u=tr[u].chl[s[i]-'a'];
}
tr[u].b=true;
}
inline bool find_pre(char s[]){
int u=0,l=strlen(s+1);
for(int i=1;i<=l;++i){
if(!tr[u].chl[s[i]-'a'])
return false;
u=tr[u].chl[s[i]-'a'];
}
return true;
}
AC自动机
#define N 105
#define T 10005
#define M 1350005
struct trie{
int chl[26],nxt;bool b;
}tr[T];
int tot[T],cnt;
queue<int> q;
inline void insert(char s[]){
int u=0,l=strlen(s+1);
for(int i=1;i<=l;++i){
if(!tr[u].chl[s[i]-'a'])
tr[u].chl[s[i]-'a']=++cnt;
u=tr[u].chl[s[i]-'a'];
}
tr[u].b=true;
}
inline void get_nxt(){
for(int i=0;i<26;++i)
if(tr[0].chl[i])
q.push(tr[0].chl[i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0,j,c;i<26;++i){
if(c=tr[u].chl[i]){
q.push(c);j=tr[u].nxt;
while(j&&!tr[j].chl[i])
j=tr[j].nxt;
tr[c].nxt=tr[j].chl[i];
}
}
}
}
//统计每个字符串在文章s中出现次数
inline void tot(char s[]){
int l=strlen(s+1);
for(int i=1,j=0;i<=l;++i){
while(j&&!tr[j].chl[s[i]-'a'])
j=tr[j].nxt;
if(tr[j].chl[s[i]-'a'])
j=tr[j].chl[s[i]-'a'];
if(j) ++t[j];
}
for(int i=cnt;i;--i)
if(tr[i].nxt) t[tr[i].nxt]+=t[i];
}
字典序
后缀数组
时间复杂度:\(O(nlogn)\)
#define N 200005
int a[N],sa[N],rk[N],ht[N],fir[N],sec[N],bu1[N],bu2[N],tmp[N],m;//第i小
inline void getSA(){
memset(bu1,0,sizeof(bu1));
for(int i=1;i<=m;++i) ++bu1[a[i]];
for(int i=1;i<=m;++i) bu1[i]+=bu1[i-1];
for(int i=m;i;--i) sa[bu1[a[i]]--]=i;
rk[sa[1]]=1;
for(int i=2;i<=m;++i){
rk[sa[i]]=rk[sa[i-1]];
if(a[sa[i]]!=a[sa[i-1]]) ++rk[sa[i]];
}
for(int t=1;rk[sa[m]]<m;t<<=1){
memset(bu1,0,sizeof(bu1));
memset(bu2,0,sizeof(bu2));
for(int i=1;i<=m;++i){
++bu1[fir[i]=rk[i]];
++bu2[sec[i]=((i+t>m)?0:rk[i+t])];
}
for(int i=1;i<=m;++i) bu2[i]+=bu2[i-1];
for(int i=m;i;--i) tmp[bu2[sec[i]]--]=i;
for(int i=1;i<=m;++i) bu1[i]+=bu1[i-1];
for(int i=m;i;--i) sa[bu1[fir[tmp[i]]]--]=tmp[i];
rk[sa[1]]=1;
for(int i=2;i<=m;++i){
rk[sa[i]]=rk[sa[i-1]];
if(fir[sa[i]]!=fir[sa[i-1]]||sec[sa[i]]!=sec[sa[i-1]]) ++rk[sa[i]];
}
}
for(int i=1,j,k=0;i<=m;++i) {
if(k) --k;
j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&a[i+k]==a[j+k]) ++k;
ht[rk[i]]=k;
}
}
回文串
manacher
时间复杂度:\(O(n)\)
#define N 200005
using namespace std;
int r[N],m,n,mx,id,ans;
char a[N];
inline int manacher(){
for(int i=n;i;--i){
a[i<<1]=a[i];
a[i<<1|1]='#';
}
n=n<<1|1;mx=id=0;
a[0]='$';a[1]=a[n+1]='#';
for(int i=1;i<=n;++i){
r[i]=i<mx?min(r[(id<<1)-i],mx-i):1;
while(a[i+r[i]]==a[i-r[i]]) ++r[i];
if(i+r[i]>mx) mx=i+r[i],id=i;
ans=max(ans,r[i]-1);
}
return ans;
}
博弈论
Nim Game
n堆石子, 两个人轮流操作, 每次可从任意一堆中取出任意多>0的石子, 无法操作者输.
- 结论 : 将所有石子数异或起来, 结果不为\(0\) , 先手胜; 反之, 先手负.
- 证明 :
- 石子数都为 \(0\) 为必败态.
- 若当前异或和为 \(0\) , 则无论怎么取, 取后异或和不为 \(0\) .
- 若当前异或和 \(k\) (设最高位为 \(x\) ) 不为 \(0\) , 则必存在石子数 \(a_i\) 中位 \(x\) 为 \(1\) , 此时 \(a_i\;xor\;k<a_i\) .
即取 \(a_i\;xor\;k\) 可使异或和为 \(0\) .
Nimk Game
n堆石子, 两个人轮流操作, 每次可从任意\(\small{\leq}\)k堆中取出任意多>0的石子, 无法操作者输.
- 结论 : 将所有石子数的每一位异或起来 \(mod\;(k+1)\) , 若结果都为 \(0\) , 先手负; 反之, 先手胜.
- 证明 :
- 石子数都为 \(0\) 为必败态.
- 若当前每一位的异或和都为 \(0\) , 则无论怎么取, 取后必存在某位的异或和不为 \(0\).
- 若当前存在某位的异或和不为 \(0\) , 从高位往低位确定每堆石子取的个数.
记 \(m\) 为当前位取 \(0,1\) 都可的堆数 ( 如果某为 \(1\) 的位取 \(0\) , 则比其低的位可随意取值 ) , \(n\) 为这一位除这 \(m\) 堆外, 当前位为 \(1\) 的堆数 \(mod\;(k+1)\) 的值.
若 \(n+m\leq{k}\) , 则直接使这 \(n\) 堆此位取 \(0\) , 其他不变 , \(m=n+m\) 即可;
否则 , \(n+m>k\) 即 \(n+m\geq{k+1}\) , 此时 \(n\geq{k+1-m}\) , 在这 \(m\) 堆 \(k+1-m\) 位取 \(1\) , 其余取 \(0\) ,其他不变即可.
Bash Game
n堆石子,两个人轮流操作,每次可从任意一堆中取出[1,m]个石子,无法操作者输.
-
结论 : 将所有石子数 \(mod\;(m+1)\) 异或起来,结果不为\(0\) , 先手胜; 反之, 先手负.
-
证明 :
- 石子数都为 \(0\) 为必败态.
- 若当前异或和为 \(0\) , 则无论怎么取, 取后异或和不为 \(0\) .
- 若当前异或和 \(k\) (设最高位为 \(x\) ) 不为 \(0\) , 则必存在石子数 \(a_i\;mod\;(m+1)\) 中位 \(x\) 为 \(1\) , 此时 \(a_i\;mod\;(m+1)\;xor\;k<a_i\) .
即取 \(a_i\;mod\;(m+1)\;xor\;k\) 可使异或和为 \(0\) .
Staircase Nim
n阶台阶, 每次可以从一个台阶上拿掉任意数量石子放到下一层台阶, 无法操作者输 ( 第1级可以往地面上放 ) .
- 结论 : 将所有奇数级异或起来, 结果不为\(0\) , 先手胜; 反之, 先手负.
- 证明 :
- 台阶上石子数都为 \(0\) 为必败态.
- 若当前奇数级异或和为 \(0\) , 则无论是移奇数级到偶数级还是移偶数级到奇数级, 取后异或和不为 \(0\) .
- 若当前奇数级异或和为 \(0\) , 则移奇数级到偶数级后, 异或和不为 \(0\) .
STL
set
insert(x)
erase(x)
clear()
count(x)
find(value) //找不到返回end
(*set0.lower_bound(x))
for(set<type>::iterator i=set0.begin();i!=setOfStr.end();++i)
// the element (*i)
set<int,less<int> > 升序
set<int,greater<int> > 降序
pair
pair<int,int> > a;
a=make_pair(v1,v2);
a.first a.second
- 有比较操作
map
map<key,value>
at(key)
count(key)
find(key)
insert(pair)
bitset
bitset<N> set0;
&=,|=,^=,<<=,>>=,==,!=
&,|,^,<<,>>,~
set() //将所有位全部设成 1
reset() //将所有位全部设成0
flip(); //将所有位翻转(0变成1,1变成0)
set(size_t pos, bool val = true) //将第 pos 位设为 val
reset (size_t pos) //将第 pos 位设成 0
flip(size_t pos) //翻转第 pos 位
set0[pos]
count() //计算 1 的个数
size () //返回总位数
test(size_t pos) //测试第 pos 位是否为 1
any() //判断是否有某位为1
none() //判断是否全部为0
queue
inline bool bfs(int u){
memset(dep,0,sizeof(dep));
q.push(u);dep[s]=1;
while(!q.empty()){
u=q.front();q.pop();
for(int i=g[u];i;i=e[i].nxt)
if(e[i].f>0&&!dep[e[i].to]){
q.push(e[i].to);
dep[e[i].to]=dep[u]+1;
}
}
return dep[t];
}
priority_queue
默认大根堆,重载<。
priority_queue<int,vector<int>,greater<int> > q;//小根堆
vector
for(vector<int>::iterator i=v.begin();i!=v.end();++i)
for(vector<int>::reverse_iterator i=v.rbegin();i!=v.rend();++i)
find(begin,end,value) //找不到返回end
二维动态数组
int (*a)[m]=new int[n][m];
delete []a;
vector<vector<int> > a(n);
for(int i=0;i<n;++i)
a[i].resize(m);
list
//unique和sort可自定义cmp
auto cmp1=[](const int& a, const int& b)->bool{return a%K==b%K;};
list1.unique(cmp1);
auto cmp2=[](const int&a, const int& b)->bool{return a>b;};
list1.sort(cmp2);//numbers.sort(greater<>());升序