自用模板积累(持续更新)

前言

带*为下标从1开始,否则为从0开始

常用框架

//<iomanip><sstream><queue>
#define ll long long
#define vec vector<ll>
#define pb push_back
#define all(a) (a).begin(),(a).end()
#define x first
#define y second
#define mk make_pair
#define pii pair<ll,ll>
#define db(x) cout<<(#x)<<':'<<(x)
//线段树宏定义可能弃用
#define tinfo ll rt,ll l,ll r
#define sl rt<<1,l,mid
#define sr rt<<1|1,mid+1,r
#define mid (l+r>>1)<<endl;
const double pi=acos(-1.0);

void INIT() {
   
}

void solve() {
   
}

int main() {
   
#ifndef ONLINE_JUDGE
  while(true)
#endif
  {
   
#ifndef ONLINE_JUDGE
    INIT();
#endif
// ll t;
// scanf("%lld",&t);
// while(t--)
    solve();
  }
  return 0;
}

基础算法

快速排序*

void qsort(ll l,ll r)
{
   
  if(l>=r) return;
  ll val=arr[l],p=l,q=r;
  while(p<q)
  {
   
    while(p<q&&arr[q]>val) q--;
    if(p<q){
   arr[p]=arr[q];p++;}
    while(p<q&&arr[p]<val) p++;
    if(p<q){
   arr[q]=arr[p];q--;}
  }
  arr[p]=val;
  qsort(l,p-1);
  //if(m-1>p) 求第m小数
  qsort(p+1,r);
}

并查集

无权+路径压缩+启发式合并

ll find(ll x) {
   return x==pre[x]?x:pre[x]=find(pre[x]);}
void merge(ll u,ll v) {
   
  ll fu=find(u),fv=find(v);
  if(height[fu]>height[fv]) pre[fv]=fu;
  else pre[fu]=fv;
  if(height[fu]==height[fv]) ++height[fv];
}

带权+路径压缩

ll find(ll x) {
   
  if(x==pre[x]) return x;
  ll fx=pre[x];
  pre[x]=find(pre[x]);
  val[x]+=val[fx];
  return pre[t];
}
void unionn(ll a,ll b,ll value) {
   
  ll fa=pre[a],fb=pre[b];
  val[fa]=val[b]+value-val[a];
  pre[fa]=fb;
}

高精度(输入输出均为逆序字符串)

加/乘 通用模板

string [add@mult](string aa,string bb) {
   
  int cnta=aa.length(),cntb=bb.length(),cntc=[max(cnta,cntb)+1@cnta+cntb+1];
  int a[N],b[N],c[N];
  clr(a,0);clr(b,0);clr(c,0);
  rep(i,0,cnta) a[i]=aa[i]-'0';
  rep(i,0,cntb) b[i]=bb[i]-'0';
  [rep(i,0,cntc) c[i]=a[i]+b[i];@
  rep(i,0,cnta) rep(j,0,cntb) c[i+j]+=a[i]*b[j];]
  ll t=0;
  rep(i,0,cntc) {
   
    c[i]+=t;
    t=c[i]/10;
    c[i]%=10;
  }
  while(c[cntc]==0&&cntc) --cntc;
  string cc;
  rep(i,0,cntc+1) cc+=c[i]+'0';
  return cc;
}

数论

拓展欧几里得

ll exgcd(ll a,ll b,ll &x,ll &y) {
   
  if(!b) {
   
    x=1;
    y=0;
    return a;
  }
  ll t=exgcd(b,a%b,y,x);
  y-=(a/b)*x;
  return t;
}

逆元(拓欧,最小非负整数解,特判)

//qpow(val,p-2),费马小定理(val的逆元,p为质数)
//i^-1=-p/i*(p%i)^-1
ll inel(ll a,ll b,ll c,ll &x,ll &y) {
   
  ll gcd=exgcd(a,b,x,y);
  if(c%gcd!=0) return 0;
  ll t;
  if(b) {
   
    t=b/gcd;
    x=(x*c/gcd%t+abs(t))%t;
  }
  if(a) {
   
    t=a/gcd;
    y=(y*c/gcd%t+abs(t))%t;
  }
  return gcd;
}

质数

线性欧拉质数筛

bitset<N> notprime;
vec prime;
ll phi[N];
void getprime(ll n) {
   
  phi[1]=1;
  for(ll i=2; i<=n; ++i) {
   
    if(!notprime[i]) {
   
      prime.pb(i);
      phi[i]=i-1;
    }
    for(ll j=0; j<prime.size()&&i*prime[j]<=n; ++j) {
   
      notprime[i*prime[j]]=true;
      if(i%prime[j]==0) {
   
        phi[i*prime[j]]=phi[i]*prime[j];
        break;
      }
      phi[i*prime[j]]=phi[i]*(prime[j]-1);
    }
  }
}

质数个数

ll count(ll x) {
   
  ll ans=0;
  for(ll i=0; i<prime.size()&&prime[i]*prime[i]<=x; ++i) {
   
    for(; x%prime[i]==0; ++ans) x/=prime[i];
  }
  if(x!=1) ++ans;
  return ans;
}

动态规划

状压dp

位运算

// 获取 a 的第 b 位,最低位编号为 0
int getBit(int a, int b) {
    return (a >> b) & 1; }
// 将 a 的第 b 位设置为 0 ,最低位编号为 0
int unsetBit(int a, int b) {
    return a & ~(1 << b); }
// 将 a 的第 b 位设置为 1 ,最低位编号为 0
int setBit(int a, int b) {
    return a | (1 << b); }
// 将 a 的第 b 位取反 ,最低位编号为 0
int flapBit(int a, int b) {
    return a ^ (1 << b); }
ll lowbit(ll x) {
   return x&(-x);}

内建函数

转自OI Wiki

__builtin_clzll(n)//前导0的个数
31-__builtin_clzll(n);//取对数
__builtin_ffsll(n)//末尾最后一个1的位置,位置从1开始
__builtin_ctzll(n)//末尾连续0的个数
__builtin_clrsbll(n)//符号位为0时返回n前导0的个数-1,否则返回前导1的个数-1。
int __builtin_parityll(unsigned int x)//1的个数的奇偶性。
__builtin_popcountll(n);//二进制1的个数

字符串

Trie树

struct node {
   
  ll nxt[26];
} trie[N];
ll cont;

void insert(string s) {
   
  ll p=0;
  for(ll i=0; i<s.length(); ++i) {
   
    ll &to=trie[p].nxt[str[i]-'a'];
    if(to) p=to;
    else {
   
      p=to=++cont;
      memset(trie+p,0,sizeof trie[p]);
    }
  }
}

ll query(string s) {
   
  ll p=0,ans=0;
  for(ll i=0; i<s.length(); ++i) {
   
    ll &to=trie[p].nxt[s[i]-'a'];
    if(to) {
   
      p=to;
      ans+=i位置匹配成功;
    } else 匹配失败;
  }
  ans+=匹配成功;
  return ans;
}

void init() {
   
  memset(trie,0,sizeof trie[0]);
  cont=0;
}

拓展kmp(标准kmp板子?你还没记住?)

//pre[i]为i位置(右开)最长公共前后缀
//if(t[++p]==t[++q]) pre[q]=pre[p]; else pre[q]=p; 优化
//p=pre[p];重叠 p=0;不重叠
//lent-nxt[lent]首尾添加能构成的最小循环节长度
//注意体会与manacher思想的相似性,nxt决定ext,带-x的下标一般是模式串的,为了与主串统一到0下标开始
vec exprekmp(string s) {
   
  ll len=s.length();
  vec nxt(len);
  nxt[0]=len;
  for(ll i=1,l=0,r=0; i<len; ++i) {
   
    if(i+nxt[i-l]>=r) {
   
      if(i>r) r=i;
      while(r<len&&s[r]==s[r-i]) ++r;
      nxt[i]=r-i;
      l=i;
    } else nxt[i]=nxt[i-l];
  }
  return nxt;
}
vec exkmp(string s,string t) {
   
  ll lens=s.length(),lent=t.length();
  vec nxt=exprekmp(t),ext(lens);
  for(ll i=0,l=0,r=0; i<lens; ++i) {
   
    if(i+nxt[i-l]>=r) {
   
      if(i>r) r=i;
      while(r<lens&&s[r]==t[r-i]) ++r;
      ext[i]=r-i;
      l=i;
    } else ext[i]=nxt[i-l];
  }
  return ext;
}

manacher

//?奇下标偶长度,字符偶下标和奇长度,与前一个?结合
//中间位置(右开)=原始下标:i-1>>1;原始长度:len-1>>1
//起始位置:i-rds[i]>>1,结束位置(右开):i+rds[i]-1>>1;
//回文长度:rds[i]-1;
vec manacher(string raw) {
   
  string s="!?";
  for(auto v:raw) {
   
    s+=v;
    s+='?';
  }
  ll len=s.length();
  vec rds(len);
  for(ll i=0,l=0,r=0; i<len; ++i) {
   
    rds[i]=r>i?min(rds[2*l-i],r-i):1;
    while(s[i+rds[i]]==s[i-rds[i]]) ++rds[i];
    if(i+rds[i]>r) r=i+rds[l=i];
  }
  return rds;
}

图论

二分图最大匹配(前向星)

ll hd[N],pre[N];
bitset<N> vis;
ll to[2*N],nxt[2*N];
ll n,m,k,cnt;//n匹配点数量,m被匹配点数量,k边数量
bool dfs(ll u) {
   
  for(ll i=hd[u]; i; i=nxt[i]) {
   
    if(vis[to[i]]) continue;
    vis[to[i]]=true;
    if(!pre[to[i]]||dfs(pre[to[i]])) {
   
      pre[to[i]]=u;
      return true;
    }
  }
  return false;
}
    while(k--) {
   
      ll u,v;
      cin>>u>>v;
      add(u,v+n);
      add(v+n,u);
    }
    ll cnt=0;
    for(ll i=1; i<=n; ++i) {
   
      vis.reset();
      cnt+=dfs(i);
    }
    cout<<cnt<<endl;

最短路径

Dij

邻接矩阵

ll n,s,e;
ll dis[maxn],g[maxn][maxn];
bool vis[maxn];
//g[ta][tb]=g[tb][ta]=min(g[ta][tb],tc);
  dis[s]=0;
  while(s!=e)
  {
   
    ll mindis=inf,idx;
    vis[s]=true;
    rep(i,0,n)
    {
   
      dis[i]=min(dis[i],dis[s]+g[s][i]);
      if(!vis[i]&&dis[i]<mindis) mindis=dis[idx=i];
    }
    if(mindis==inf) break;
    s=idx;
  }
  cout<<dis[e]<<endl;

堆优化+前向星

ll n,m;
ll dis[maxn],hd[maxn];
bool vis[maxn];
ll cnt;
ll to[maxm],w[maxm],nxt[maxm];
ll ta,tb,tc,s,e;
struct node {
   
  ll s,w;
  bool operator<(const node &a)const {
   
    return a.w<w; //与condition()同步
  }
};
priority_queue<node> que;

  //init
  memset(dis,inf,sizeof dis);
  memset(vis,0,sizeof vis);
  memset(hd,0,sizeof hd)//*******WARNING********
  cnt=0;
  //in
  cin>>n>>m;
  rep(i,0,m) {
   
    cin>>ta>>tb>>tc;

    w[++cnt]=tc;
    to[cnt]=tb;
    nxt[cnt]=hd[ta];
    hd[ta]=cnt;
  }
  cin>>s>>e;
  //dij
  dis[s]=0;
  que.push(node{
   s,0});
  while(!que.empty()) {
   
    node t=que.top();
    que.pop();
    if(vis[t.s]) continue;//易忘
    vis[t.s]=true;
    for(ll i=hd[t.s]; i; i=nxt[i]) {
   
      if(dis[t.s]+w[i]<dis[to[i]])//易忘
        //if(condition(stucture(dis[pnt],w[i]),dis[to[i]])) <条件>的<结构> ex. min(dis[pnt],w[i])>dis[to[i]] 最大的路径的边的最小值
      {
   
        dis[to[i]]=dis[t.s]+w[i];
        que.push(node{
   to[i],dis[to[i]]});
      }
    }
  }
  //out
  cout<<dis[e]<<endl;

最小生成树(Prim)(邻接矩阵无优化)*

int prim() {
   
  int ans=0;
  vis.reset();
  dis[1]=0;
  vis[1]=true;
  for(int i=1; i<=n; i++) {
   
    dis[i]=graph[1][i];
  }
  for(int i=0; i<n-1; i++) {
   
    int mindis=inf,index;
    for(int j=1; j<=n; j++) {
   
      if(vis[j]==false&&dis[j]<mindis) {
   
        mindis=dis[j];
        index=j;
      }
    }
    ans+=mindis;
    vis[index]=true;
    for(int j=1; j<=n; j++) {
   
      if(vis[j]==false) {
   
        dis[j]=min(graph[index][j],dis[j]);
      }
    }
  }
  return ans;
}

双向链表遍历*

vector<ll> graph[N];
ll n;
void dfs(ll p,ll fa) {
   
  if(graph[p].size()==1) return;//判断叶节点
  for(auto v:graph[p]) {
   
    if(v==fa) continue;
    dfs(v,p);
  }
}
  cin>>n;
  graph[0].push_back(1);
  graph[1].push_back(0);
  for(ll i=0; i<n-1; ++i) {
   
    ll a,b;
    cin>>a>>b;
    graph[a].push_back(b);
    graph[b].push_back(a);
  }
  dfs(1,0);

二叉树

先序遍历+中序遍历->后序遍历(dfs)

int n;
int pre[maxn];
int in[maxn];
bool head;
void build(int la,int ra,int lb,int rb) {
   
  if(la==ra) return;
  if(la+1==ra) {
   
    if(head) {
   
      printf("%d",pre[la]);
      head=false;
    } else printf(" %d",pre[la]);
    return;
  }
  int p=lb;
  while(in[p]!=pre[la]&&p<rb)p++;
  build(la+1,la+1+p-lb,lb,p);
  build(la+1+p-lb,ra,p+1,rb);
  if(head) {
   
    printf("%d",pre[la]);
    head=false;
  } else printf(" %d",pre[la]);
}

  while(~scanf("%d",&n)) {
   
    head=true;
    rep(i,0,n) scanf("%d",&pre[i]);
    rep(i,0,n) scanf("%d",&in[i]);
    build(0,n,0,n);
    puts("");
  }

查询/修改

树状数组*

ll tree[maxn];
void update(ll x,ll v) {
   
  while(x<=n) {
   
    tree[x]+=v;
    x+=lowbit(x);
  }
}
ll getsum(ll x) {
   
  ll ans=0;
  while(x) {
   
    ans+=tree[x];
    x-=lowbit(x);
  }
  return ans;
}
ll query(ll l,ll r) {
   
  return getsum(r)-getsum(l-1);
}

线段树

void pushup(ll rt) {
   
  //要维护操作
  tree[rt]=operation(tree[rt<<1],tree[rt<<1|1]);
}
ll query(tinfo,ll tl,ll tr) {
   
  if(tl<=l&&tr>=r) return tree[rt];
  ll ans;
  //pushdown(rt,l,r); 区间更新使用
  if(tl<=mid) ans operation=query(sl,tl,tr);
  if(tr>mid) ans operation=query(sr,tl,tr);
  return ans;
}

标准线段树(带区间更新)*

#define mid (l+r>>1)
#define sl rt<<1,l,mid
#define sr rt<<1|1,mid+1,r
#define tdata 1,1,n
#define tinfo ll rt,ll l,ll r

ll tree[maxn<<2];
ll lzy[maxn<<2];
ll arr[maxn];
ll n;

void pushdown(tinfo) {
   
  if(!lzy[rt]) return;
  tree[rt<<1]+=lzy[rt]*(mid-l+1);
  tree[rt<<1|1]+=lzy[rt]*(r-mid);
  lzy[rt<<1]+=lzy[rt];
  lzy[rt<<1|1]+=lzy[rt];
  lzy[rt]=0;
}
void build(tinfo) {
   
  if(l==r) {
   
    tree[rt]=arr[l];
    return;
  }
  build(sl);
  build(sr);
  pushup(rt);
}
//单点更新
void update(tinfo,ll p,ll w) {
   
  if(l==r) {
   
  	//更新操作
    tree[rt] operation=w;
    return ;
  }
  if(p>mid) update(sr,p,w);
  else update(sl,p,w);
  pushup(rt);
}
//区间更新
void update(tinfo,ll tl,ll tr,ll v) {
   
  if(tl<=l&&tr>=r) {
   
    tree[rt]=(r-l+1)*v;
    lzy[rt]=v;
    return;
  }
  pushdown(rt,l,r);
  if(tl<=mid)update(sl,tl,tr,v);
  if(tr>mid) update(sr,tl,tr,v);
  pushup(rt);
}

可持久化线段树(暂不支持区间更新)*

#define tinfo ll rt,ll l,ll r
#define sl sonl[rt],l,mid
#define sr sonr[rt],mid+1,r
#define mid (l+r>>1)
const ll M=N*(64-__builtin_clzll(N))+10;

ll arr[N];
ll sonl[M],sonr[M],tree[M],cnt;

ll build(tinfo) {
   
  rt=++cnt;
  if(l==r) {
   
    tree[rt]=arr[l];
    return rt;
  }
  sonl[rt]=build(sl);
  sonr[rt]=build(sr);
  pushup(rt);
  return rt;
}

ll update(tinfo,ll p,ll v) {
   
  ll rtnew=++cnt;
  if(l==r) {
   
    tree[rtnew]=v;
    return cnt;
  }
  if(p<=mid) {
   
    sonl[rtnew]=update(sl,p,v);
    sonr[rtnew]=sonr[rt];
  } else {
   
    sonl[rtnew]=sonl[rt];
    sonr[rtnew]=update(sr,p,v);
  }
  pushup(rtnew);
  return rtnew;
}

稀疏表(Sparse Table)

ll st[20][N];
ll log(ll x) {
   
  return 63-__builtin_clzll(x);
}
void stable() {
   
  ll logn=log(n);
  for(int i=0; i<n; i++)
  st[0][i]=cha[i];
  for(int i=1; i<=logn; i++)
  for(int j=0; j+(1<<i)<=n; j++)//<=号
  st[i][j]=__gcd(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
ll query(ll l,ll r) {
   
  ll logn=log(r-l);
  return __gcd(st[logn][l],st[logn][r-(1<<logn)]);
}
全部评论

相关推荐

11-01 08:48
门头沟学院 C++
伤心的候选人在吵架:佬你不要的,能不能拿户口本证明过户给我。。球球了
点赞 评论 收藏
分享
10-25 00:32
香梨想要offer:感觉考研以后好好学 后面能乱杀,目前这简历有点难
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务