面试前夕oi挣扎式复习
-
线段树
void PushUp(ll rt){ Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1]; } void Build(ll l,ll r,ll rt){ if(l==r){ Sum[rt]=A[l]; return ; } ll m=(l+r)>>1; Build(l,m,rt<<1); Build(m+1,r,rt<<1|1); PushUp(rt); } void PushDown(ll rt,ll ln,ll rn){//下推标记 if(Add[rt]){ Add[rt<<1]+=Add[rt]; Add[rt<<1|1]+=Add[rt]; Sum[rt<<1]+=Add[rt]*ln; Sum[rt<<1|1]+=Add[rt]*rn; Add[rt]=0;//清除标记 } } void Update(ll L,ll R,ll C,ll l,ll r,ll rt){//区间修改 if(L<=l&&r<=R){//如果[l,r]区间完全在操作区间[L,R]以内 Sum[rt]+=C*(r-l+1); Add[rt]+=C; return ; } ll m=(l+r)>>1; PushDown(rt,m-l+1,r-m); if(L<=m)Update(L,R,C,l,m,rt<<1); if(R>m) Update(L,R,C,m+1,r,rt<<1|1); PushUp(rt); } ll Query(ll L,ll R,ll l,ll r,ll rt){//区间求和 if(L<=l&&r<=R){//在区间内直接返回 return Sum[rt]; } ll m=(l+r)>>1; PushDown(rt,m-l+1,r-m); ll ANS=0; if(L<=m)ANS+=Query(L,R,l,m,rt<<1); if(R>m) ANS+=Query(L,R,m+1,r,rt<<1|1); return ANS; }
-
树状数组
inline ll lowbit(ll x) {return x&-x;} inline void add(int x,ll k) { while(x<=n) { a[x]+=k;x+=lowbit(x); } } inline ll find(int x) { ll sum=0; while(x) { sum+=a[x];x-=lowbit(x); } return sum; }
-
tarjan缩点
void dfs(int u) { dfn[u]=low[u]=++tot; vis[u]=1;sta[++top]=u; for(int j=head[u];j;j=e[j].nxt) { int v=e[j].v; if(!dfn[v]) { dfs(v);low[u]=min(low[u],low[v]); } else if(vis[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { int x; while(1) { x=sta[top--];vis[x]=0; fa[x]=u;if(x==u)break; a[u]+=a[x]; } } }
-
线性基
for(int i=1;i<=n;i++) { scanf("%lld",&x); for(int k=62;k>=0;k--)if((1LL<<k)&x) { if(!xx[k]) {xx[k]=x;break;} x^=xx[k]; } } ll ans=0; for(int k=62;k>=0;k--) if((ans^xx[k])>ans) ans^=xx[k];
-
ST表
lg[0]=-1; for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1; for(int i=1;i<=n;i++) { scanf("%d",&f[0][i]); } for(int j=1;j<=20;j++) for(int i=1;i+(1<<j)-1<=n;i++) f[j][i]=max(f[j-1][i],f[j-1][i+(1<<(j-1))]); int x,y,k; while(m--) { scanf("%d%d",&x,&y); k=lg[y-x+1]; printf("%d\n",max(f[k][x],f[k][y-(1<<k)+1])); }
-
并查集
inline int get(int x) {return x==fa[x]?x:fa[x]=get(fa[x]);}
-
欧拉筛
void GetPrime(int n)//筛到n { memset(isPrime, 1, sizeof(isPrime)); //以“每个数都是素数”为初始状态,逐个删去 isPrime[1] = 0;//1不是素数 for(int i = 2; i <= n; i++) { if(isPrime[i])//没筛掉 Prime[++cnt] = i; //i成为下一个素数 for(int j = 1; j <= cnt && i*Prime[j] <= n/*不超上限*/; j++) { //从Prime[1],即最小质数2开始,逐个枚举已知的质数,并期望Prime[j]是(i*Prime[j])的最小质因数 //当然,i肯定比Prime[j]大,因为Prime[j]是在i之前得出的 isPrime[i*Prime[j]] = 0; if(i % Prime[j] == 0)//i中也含有Prime[j]这个因子 break; //重要步骤。见原理 } } }
-
网络流最大流
#include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e4+50,M=1e5+50; struct pp { int v,nxt,c; }e[M<<2]; int tot=1,n,m,st,ed; int head[N],dep[N],gap[N]; void add(int u,int v,int c) { e[++tot].nxt=head[u];head[u]=tot; e[tot].v=v;e[tot].c=c; e[++tot].nxt=head[v];head[v]=tot; e[tot].v=u;e[tot].c=0; } void bfs() { memset(dep,-1,sizeof(dep)); gap[dep[ed]=0]=1; queue<int> q;q.push(ed); while(!q.empty()) { int u=q.front(); q.pop(); for(int j=head[u];j;j=e[j].nxt) { int v=e[j].v; if(dep[v]!=-1) continue; q.push(v);gap[dep[v]=dep[u]+1]++; } } } int dfs(int u,int flow) { if(u==ed) return flow; int s=0,val; for(int j=head[u];j;j=e[j].nxt) { int v=e[j].v,c=e[j].c; if(c<=0||dep[v]!=dep[u]-1)continue; s+=(val=dfs(v,min(c,flow-s))); e[j].c-=val;e[j^1].c+=val; if(s==flow) return s; } gap[dep[u]]--; if(gap[dep[u]]==0) dep[st]=n+1; gap[++dep[u]]++; return s; } int main() { scanf("%d%d%d%d",&n,&m,&st,&ed); for(int i=1,u,v,c;i<=m;i++) { scanf("%d%d%d",&u,&v,&c); add(u,v,c); } bfs(); int res=0; while(dep[st]<n) res+=dfs(st,233333333L); printf("%d\n",res); return 0; }
-
快速矩阵幂
struct Mat { ll m[N][N]; Mat operator = (int x) { for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) m[i][j]=(i==j&&x); return *this; } Mat operator * (Mat b) { Mat a=*this,c;c=0; for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) (c.m[i][j]+=(a.m[i][k]*b.m[k][j]))%=MOD; return c; } Mat operator ^ (ll b) { Mat a=*this,c;c=1; for(;b;b>>=1) {if(b&1) c=c*a;a=a*a;} return c; } } d; void print(Mat a) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) printf("%lld ",a.m[i][j]);printf("\n"); } }
-
最小费用最大流
#include<queue> #include<cstdio> #include<cstring> using namespace std; const int N=5050,M=50050; struct pp { int v,nxt,d,c; }e[M<<1]; int head[N],flow[N],tot=1,pre[N],dis[N]; bool vis[N]; int n,m,st,ed; long long res,ans; int min(int a,int b) { return a>b?b:a; } void add(int u,int v,int c,int d) { e[++tot].nxt=head[u];head[u]=tot; e[tot].v=v;e[tot].d=d;e[tot].c=c; e[++tot].nxt=head[v];head[v]=tot; e[tot].v=u;e[tot].d=-d;e[tot].c=0; } bool spfa() { queue<int> q;q.push(st); memset(dis,0x3f,sizeof(dis)); dis[st]=0;flow[st]=233333333L; while(!q.empty()) { int u=q.front();q.pop();vis[u]=0; for(int j=head[u];j;j=e[j].nxt) { int v=e[j].v,c=e[j].c,d=e[j].d; if(c<=0) continue; if(dis[v]>dis[u]+d) { dis[v]=dis[u]+d; pre[v]=j; flow[v]=min(flow[u],c); if(!vis[v]) vis[v]=1,q.push(v); } } } if(dis[ed]==dis[0]) return 0; int mn=233333333L; for(int x=ed,j;x!=st;x=e[j^1].v) j=pre[x],mn=min(mn,flow[x]); res+=1LL*mn*dis[ed]; ans+=1LL*mn; for(int x=ed,j;x!=st;x=e[j^1].v) { j=pre[x]; e[j].c-=mn; e[j^1].c+=mn; } return 1; } int main() { scanf("%d%d%d%d",&n,&m,&st,&ed); for(int i=1;i<=m;i++) { int u,v,c,d; scanf("%d%d%d%d",&u,&v,&c,&d); add(u,v,c,d); } while(spfa()); printf("%lld %lld\n",ans,res); return 0; }
-
最近公共祖先LCA
void dfs(int u,int la) { f[u][0]=la;dep[u]=dep[la]+1; for(int i=1;i<=lg[dep[u]];i++) f[u][i]=f[f[u][i-1]][i-1]; for(int j=head[u];j;j=e[j].nxt) { int v=e[j].v; if(v==la) continue; dfs(v,u); } } int lca(int x,int y) { if(dep[x]<dep[y]) x^=y^=x^=y; while(dep[x]>dep[y]) x=f[x][lg[dep[x]-dep[y]-1]]; if(x==y) return x; for(int k=lg[dep[x]];k>=0;k--) if(f[x][k]!=f[y][k]) x=f[x][k],y=f[y][k]; return f[x][0]; }
-
dijkstra
void dijkstra() { memset(dis,127/3,sizeof(dis)); dis[s]=0; q.push((qq){s,0}); while(!q.empty()) { qq x=q.top();q.pop(); int u=x.u; if(vis[u]) continue; vis[u]=1; for(int j=head[u];j;j=e[j].nxt) { int v=e[j].to,d=e[j].d; if(dis[v]>dis[u]+d) { dis[v]=dis[u]+d; q.push((qq){v,dis[v]}); } } } }
-
spfa
void spfa() { queue<int> q; //spfa用队列,这里用了STL的标准队列 for(int i=1; i<=n; i++) { dis[i]=inf; //带权图初始化 vis[i]=0; //记录点i是否在队列中,同dijkstra算法中的visited数组 } q.push(s); dis[s]=0; vis[s]=1; //第一个顶点入队,进行标记 while(!q.empty()) { int u=q.front(); //取出队首 q.pop(); vis[u]=0; //出队标记 for(int i=head[u]; i; i=edge[i].next) //邻接表遍历,不多解释了(也可用vector代替) { int v=edge[i].to; if(dis[v]>dis[u]+edge[i].dis) //如果有最短路就更改 { dis[v]=dis[u]+edge[i].dis; if(vis[v]==0) //未入队则入队 { vis[v]=1; //标记入队 q.push(v); } } } } }
-
二分图匹配
bool Dfs(int u,int t) // t为时间戳 { for(int j=head[u];j;j=e[j].nxt) { int v=e[j].v; if(vis[v]^t) // 要是 vis 和 t 不相等说明本次 dfs 还没用到 v { vis[v]=t; if((!f[v])||Dfs(f[v],t)) { f[v]=u; return 1; } } } return 0; }
-
exgcd
void exgcd(ll a, ll b, ll& x, ll& y, ll& c) { if(!b) {y = 0; x = 1; c = a; return;} exgcd(b, a % b, y, x); y -= a / b * x; }