缺省源和模板

整这个玩意单纯只是怕打比赛时要粘板子发现找不到了。


UPD:2022/11/1 加入 math 模块中,排列组合数的边界判断,以及卢卡斯定理求大数组合数。


UPD:2022/11/6 加入了 modint 类型的 ^,^=,< 等运算。
修改了部分代码。


UPD:2022/11/9 加入 Debug 中,记录程序运行时间的语句。


UPD:2022/11/10 更换了更快的 \(O(1)\) 快速乘,不基于 unsigned long long 的自然溢出,具体参考了这篇 blog

略微改动 modint 部分,使之支持对题目给定模数取模。


UPD:2022/11/13 更改了整个 Debug 模块,并 加入记录程序使用空间的语句。

缺省源

先是头文件和一堆宏定义,一般都是带着的。

code
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using ld=long double;
using ull=unsigned long long;
#define lc p<<1
#define rc p<<1|1
#define lb(x) (x&-x)
#define pb push_back
#define ch(i) (i-'a')
#define vi vector<int>
#define F(i) (i).first
#define S(i) (i).second
#define X(i) (i>n?i-n:i)
#define pi pair<int,int>
#define id(x,y) ((x-1)*m+y)
#define cl(x) ((x).clear());
#define si(i) ((int)i.size())
#define kw(x,k) (((x)>>(k))&1)
#define all(x) (x).begin(),(x).end()
#define me(t,x) memset(t,x,sizeof(t))
#define L(i,j,k) for(int (i)=(j);i<=(k);(i)++)
#define R(i,j,k) for(int (i)=(j);i>=(k);(i)--)
#define dout(a,x) cout<<setprecision(x)<<a<<'\n';
#define ll(i,j,k,l) for(int (i)=(j);i<=(k);(i)+=(l))
#define rr(i,j,k,l) for(int (i)=(j);i>=(k);(i)-=(l))
#define FST ios::sync_with_stdio(false);cin.tie(nullptr);

再是用来Debug的一堆宏定义。
提交时把 #define DG 注释掉就行了。
如果是在 Luogu,要把 #pragma GCC optimize(2) 删掉。
就不用挨个调试代码去删了。
在调时也减少了弄混变量数组的情况。

PS:现在暂时不支持直接输出整个数组和容器。将来学会之后或许会加上。

tam 是用于测程序运行时间和空间的。
#define DE 的情况下,在程序末尾加上 return tam;,打印运行时间和使用空间。
上交时可以不注释掉(但本地会照常输出)。
测空间要定义两个 char 变量,分别在开头和结尾(反正就是把开的数组夹在中间)。

code
#define DE
#pragma GCC optimize(2)
template<typename ...Args>
void W(string X,Args&&...V){cout<<X<<"]=[";string D=""; (...,(cout<<D<<V,D=","));cout<<"]\n";}
#ifdef DE
#define tam cerr<<1e3*clock()/CLOCKS_PER_SEC<<" ms "<<(&y_y-&x_x)/1024.0/1024.0<<" MB\n",0
#define dg(...) cerr<<__func__<<":"<<__LINE__<<" [",W(#__VA_ARGS__,__VA_ARGS__)
#else
#define dg(...)
#define tam 0
#endif

随机数生成器和序列随机打乱函数,用的是 std::mt19937
需要 C++11 及以上版本。
分别是生成 \(\left[ l,r \right]\) 范围内的整数和实数。

根据测试,在开启 O2 优化时,1s 大约可以跑 \(5 \cdot 10^7\) 次整数生成或 \(7 \cdot 10^7\) 次实数生成。
时间复杂度近似 \(O(1)\)

其实是打对拍时用的。

code
mt19937 mrand(chrono::system_clock::now().time_since_epoch().count()); 
int rnd(int l,int r){
  uniform_int_distribution<>dist(l,r);
  return dist(mrand);
}double rnd(double l,double r){
  uniform_real_distribution<>dist(l,r);
  return dist(mrand);
}void dl(vi a){shuffle(all(a),mrand);}

连续读入,输出,没啥用,但还存上了。

code
namespace IO{
template<typename _Tp>void rd(_Tp &x){cin>>x;}
template<typename _Tp,typename ...Args>void rd(_Tp &x,Args &...args){rd(x),rd(args...);}
template<typename _Tp>void wr(_Tp x){cout<<x;}
template<typename _Tp,typename ...Args>void wr(_Tp x,Args ...args){wr(x),wr(args...);}
}using namespace IO;
//rd(x,y,z);
//wr(x,' ',y,'\n');
namespace FT{
  void rd(pi &x){IO::rd(F(x),S(x));}
  void rd(vi &v,int n){v.resize(n);for(int &p:v) IO::rd(p);}
  void wr(pi x,char c){IO::wr(F(x),' ',S(x),c);}
  void wr(vi v,char c){for(int p:v) IO::wr(p,c);}
}using namespace FT;

之后是一个 modint 类的封装。

改编自 \(\color{Black}{j} \color{Red}{iangly}\) 的某个提交中部分代码。

就按照普通变量类型定义即可,注意没有取模操作,不然 CE。

相同情况下常数较大。

如果固定模数,就直接对 mod 变量赋值,否则就读入 mod 变量。

后面的 math 模块是时间复杂度 \(O(n)\) 的。

没有直接只是预处理阶乘数组然后直接除是因为这样时间复杂度 \(O(n \log_2 n)\),且常数较大。

code
int mod;
#define P ((int)mod)
int O(int x){return x<0?(x+=P):x<P?x:(x-=P);}
template<class T>
T ksm(T a,ll b){T s=1;for(;b;b>>=1,a*=a) if(b&1) s*=a;return s;}
struct Z{
  int x;Z(int x=0):x(O(x)){}Z(ll x):x(O(x%P)){}
  bool operator<(const Z&b)const{return x<b.x;}
  bool operator>(const Z&b)const{return x>b.x;}
  bool operator<=(const Z&b)const{return x<=b.x;}
  bool operator>=(const Z&b)const{return x>=b.x;}
  bool operator==(const Z&b)const{return x==b.x;}
  bool operator!=(const Z&b)const{return x!=b.x;}
  int val()const{return x;}bool operator!(){return !x;}
  Z operator-()const{return Z(O(P-x));}
  Z inv()const{assert(x!=0);return ksm(*this,P-2);}
  Z &operator^=(int b){
    Z a=*this,c=1;for(;b;b>>=1,a*=a) if(b&1) c*=a;
    return x=c.x,*this;
  }Z&operator++(){return x=O(x+1),*this;}
  Z&operator--(){return x=O(x-1),*this;}
  Z&operator*=(const Z&r){x=(ll)(x)*r.x%P;return*this;}
  Z&operator+=(const Z&r){x=O(x+r.x);return*this;}
  Z&operator-=(const Z&r){x=O(x-r.x);return*this;}
  Z&operator/=(const Z&r){return*this*=r.inv();}
  Z&operator+=(const int &r){x=O(x+r);return *this;}
  Z&operator-=(const int &r){x=O(x-r);return *this;}
  Z&operator*=(const int &r){x=(ll)(x)*r%P;return *this;}
  Z&operator/=(const int &r){Z x=r;return *this*=x.inv();}
  friend Z operator*(const Z&l,const Z&r){Z s=l;s*=r;return s;}
  friend Z operator+(const Z&l,const Z&r){Z s=l;s+=r;return s;}
  friend Z operator-(const Z&l,const Z&r){Z s=l;s-=r;return s;}
  friend Z operator/(const Z&l,const Z&r){Z s=l;s/=r;return s;}
  friend Z operator*(const Z&l,const int&r){Z s=l;s*=r;return s;}
  friend Z operator+(const Z&l,const int&r){Z s=l;s+=r;return s;}
  friend Z operator-(const Z&l,const int&r){Z s=l;s-=r;return s;}
  friend Z operator/(const Z&l,const int&r){Z s=l;s/=r;return s;}
  friend Z operator^(Z a,int b){return a^=b;}
  friend istream&operator>>(istream&is,Z&a){ll v;is>>v;a=Z(v);return is;}
  friend ostream&operator<<(ostream&os,const Z&a){return os<<a.val();}
};namespace math{
  Z jc[1000100],ijc[1000100];
  void init(int x){
    jc[0]=1;L(i,1,x) jc[i]=jc[i-1]*i;
    ijc[x]=jc[x].inv();R(i,x,1) ijc[i-1]=ijc[i]*i;
  }Z C(int n,int m){return m<0||n<m?0:jc[n]*ijc[n-m]*ijc[m];}
  Z A(int n,int m){return m<0||n<m?0:jc[n]*ijc[n-m];}
  Z BC(int n,int m){return m?BC(n/P,m/P)*C(n%P,m%P):1;}
}using namespace math;

模板

线性筛质数

用的是 埃氏筛+ std::bitset

有必要的话把那个 std::vector<int> 加上,存储范围内的质数。

时间复杂度 \(O(n \log_2 \log_2 n)\),但是实际上跑的比欧拉筛要快(无论欧拉筛是否有 std::bitset 的优化)。

code
bitset<20000000>isp;vi pr;
void es(int n){
  isp.set();isp[0]=isp[1]=0;
  L(i,2,sqrt(n)) if(isp[i])
    ll(j,(i<<1),n,i) isp[j]=0;
//  L(i,2,n) if(isp[i]) pr.pb(i);
}

素数检测

用的是 Miller Rabin 质数检测 ,在 long long 范围内应该不会出现错误。

为此还专门加了一个快速幂和快速乘。

时间复杂度 long long :\(O(7 \cdot \log_2 \log_2 \log_2 n)\) int :\(O(3 \cdot \log_2 \log_2 \log_2 n)\)

code
ll qmul(ll a,ll b,ll p) {
  ll rt=a*b-((ll)((ld)a/p*b))*p;
  return rt>=p?rt-p:rt<0?rt+p:rt;
}ll power(ll a,ll b,ll p){
  ll s=1;while(b){
    if(b&1) s=qmul(s,a,p);
    a=qmul(a,a,p);b>>=1;
  }return s;
}bool mr(ll n){
  if(n<3||n%2==0) return n==2;
  ll u=n-1,t=0;while(!(u&1)) u>>=1,++t;
  vi ud;if(n<=2147483647) ud={2,7,61};
  else ud={2,325,9375,28178,450775,9780504,1795265022};
  for(ll a:ud){
    ll v=power(a,u,n);
    if(v==1||v==n-1||v==0) continue;
    L(j,1,t){
      v=qmul(v,v,n);
      if(v==n-1&&j!=t){v=1;break;}
      if(v==1) return 0;
    }if(v^1) return 0;
  }return 1;
}

网络流

费用流暂时没有整理,最大流用的是 \(\text{Dinic}\),也把边封装进去,注意每次跑时的清空。

code
struct flow{
  int st,ed,T,a[N];
  struct E{int v,w,nt;}e[N<<5];
  int fir[N],c=1;
  void I(int u,int v,int w){
    e[++c]=(E){v,w,fir[u]};fir[u]=c;
    e[++c]=(E){u,0,fir[v]};fir[v]=c;
  }int cur[N],d[N];
  bool bfs(){
    L(i,0,ed) d[i]=0,cur[i]=fir[i];
    queue<int>q;q.push(st);d[st]=1;
    while(!q.empty()){
      int u=q.front(),v;q.pop();
      E(i,u) if(!d[v=e[i].v]&&e[i].w)
        q.push(v),d[v]=d[u]+1;
    }return d[ed];
  }int dfs(int u,int fl){
    if(u==ed) return fl;int s=0,f,v;
    for(int i=cur[u];i;i=e[i].nt){
      cur[u]=i;
      if(e[i].w&&d[v=e[i].v]==d[u]+1){
        f=dfs(v,min(e[i].w,fl));
        e[i].w-=f;e[i^1].w+=f;
        s+=f;fl-=f;if(!fl) break;
      }
    }if(!s) d[u]=0;return s;
  }int dinic(){int s=0;while(bfs()) s+=dfs(st,INF);return s;}
}fl;

最短路

std::priority_queue 优化的 dijkstra,时间复杂度 \(O((n+m) \log_2 m)\)

code
struct DJ{
  vector<pi>g[N];vi dis,vis;
  priority_queue<pi>q;
  void I(int u,int v,int w){g[u].pb({v,w});}
  void init(int x,int s){
    dis.resize(s+1);fill(all(dis),INF);
    vis.resize(s+1);fill(all(vis),0);
    dis[x]=0;q.push({0,x});
  }void dij(){
    while(!q.empty()){
      int u=S(q.top());q.pop();
      if(vis[u]) continue;vis[u]=1;
      for(auto x:g[u])
        if(dis[F(x)]>dis[u]+S(x)){
          dis[F(x)]=dis[u]+S(x);
          if(!vis[F(x)]) q.push({-dis[F(x)],F(x)});
        }
    }
  }
};

LCA

比较冷门的 dfs 序求 LCA,预处理 \(O(n \log_2 n)\),查询 \(O(1)\),基于 dfs 序和 ST 表。

相比于传统的欧拉序 LCA 更好写,更短,没有双倍的常数和空间。

code
int n,m,u,v,rt;vi g[N];
struct dl{
  int dn,dfn[N],d[N],lg[N],st[19][N];
  int get(int x,int y){return d[x]<d[y]?x:y;}
  void dfs(int u,int f){
    st[0][dfn[u]=++dn]=f;d[u]=d[f]+1;
    for(int v:g[u]) if(v^f) dfs(v,u);
  }int lca(int u,int v){
    if(u==v) return u;if((u=dfn[u])>(v=dfn[v])) swap(u,v);
    int d=lg[v-u++];return get(st[d][u],st[d][v-(1<<d)+1]);
  }void init(){
    L(i,1,lg[n]) L(j,1,(n+1-(1<<i)))
      st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);
  }
};

BIT

就树状数组,加一个权值树状数组的 kth 函数。

code
struct BIT{
  int n,c[N];void add(int x,int k){for(;x<=n;x+=lb(x)) c[x]+=k;}
  int ask(int x){int s=0;for(;x;x-=lb(x)) s+=c[x];return s;}
  int kth(int k){
    int r=0,t=0;
    for(int i=lg/*log2(值域)*/,x,y;~i;i--){
      if((x=r+(1<<i))>n) continue;
      if((y=t+c[x])<k) r=x,t=y;
    }return a[r];//a 是离散化前的数组
  }
};

posted @ 2022-10-11 22:57  AIskeleton  阅读(100)  评论(0编辑  收藏  举报