IAOI 模板库

前言

编译选项:

-std=gnu++17 -O2

使用该模板时,请在程序开头加上如下语句:

#include<bits/stdc++.h>
using namespace std;

注意事项

  • 现较完善模板已有:快速 IO、并查集、ST 表、树状数组、网络最大流、扫描线、后缀数组、数论相关等内容;
  • 区间数据结构的实现中,查询区间为闭区间,而不是各大主流模板库的半闭半开区间;
  • 本模板库在部分问题上参考了 AtCoder Library 的处理方式。

Upd on \(2024.10.13\):添加了“命题相关模板”一栏。

第一部分 较完善模板

快速 IO(Fast IO)

namespace IAOI_lib{
  typedef long long ll;
  inline int read(){
    int x=0; char c=getchar(); bool f=false;
    while(!isdigit(c)){
      if(c=='-')f=true;
      c=getchar();
    }
    while(isdigit(c)){
      x=(x<<1)+(x<<3)+(c^48);
      c=getchar();
    }
    return f?-x:x;
  }
  inline void write(int x){
    if(x<0){putchar('-'); write(-x); return;}
    if(x/10)write(x/10);  putchar(x%10+48);
  }
  inline ll readll(){
    ll x=0; char c=getchar(); bool f=false;
    while(!isdigit(c)){
      if(c=='-')f=true;
      c=getchar();
    }
    while(isdigit(c)){
      x=(x<<1)+(x<<3)+(c^48);
      c=getchar();
    }
    return f?-x:x;
  }
  inline void writell(ll x){
    if(x<0){putchar('-'); write(-x); return;}
    if(x/10)writell(x/10);  putchar(x%10+48);
  }
}

功能介绍

  • int read() / long long readll():从标准输入读取一个整数;
  • int write() / long long writell():向标准输出写入一个整数。

示例代码

P9515「JOC-1A」签到题 by FFTotoro

并查集(DSU)

namespace IAOI_lib{
  template<typename T> class dsu{
    private:
      vector<T> a;
      vector<int> s;
    public:
      dsu(int n=2e5){
        a.resize(n),s.resize(n,1);
        iota(a.begin(),a.end(),0);
      }
      T leader(T x){
        return a[x]==x?x:a[x]=leader(a[x]);
      }
      inline int size(T x){
        return s[leader(x)];
      }
      inline void merge(T x,T y){
        x=leader(x),y=leader(y);
        if(x==y)return;
        if(s[x]>s[y])swap(x,y);
        s[y]+=s[x],a[x]=y;
      }
      inline bool same(T x,T y){
        return leader(x)==leader(y);
      }
  };
}

功能介绍

UPD:增加了带权功能(即维护连通块大小)。

  • dsu<T> d(int n):创建一个大小为 \(n(1\le n\le 10^8)\)(如果不添加 (int n) 则为默认大小 \(2\times 10^5\))、存储类型为 T 的并查集,时间复杂度 \(O(n)\)(下文 \(x,y\) 的范围均为 \(0\le x,y<n\));
  • T d.leader(T x):返回 \(x\) 的祖先,时间复杂度 \(O(\alpha(n))\)
  • int d.size(T x):返回 \(x\) 所在连通块(集合)的大小,时间复杂度 \(O(\alpha(n))\)
  • void d.merge(T x,T y):合并 \(x\)\(y\) 所在的连通块(集合),时间复杂度 \(O(\alpha(n))\)
  • bool d.same(T x,T y):返回 \(x\)\(y\) 是否在同一个连通块(集合)内,时间复杂度 \(O(\alpha(n))\)

示例代码

P9488 ZHY 的生成树 by FFTotoro

ST 表(Sparse Table)

namespace IAOI_lib{
  template<typename T,T(*op)(T,T)> class sparse_table{
    private:
      vector<vector<T> > s;
    public:
      sparse_table(vector<T> a){
        int k=__lg(a.size());
        s.resize(k+1,vector<T>(a.size()));
        for(int i=0;i<a.size();i++)
          s[0][i]=a[i];
        for(int i=1;i<=k;i++)
          for(int j=0;j+(1<<i)<=a.size();j++)
            s[i][j]=op(s[i-1][j],s[i-1][j+(1<<i-1)]);
      }
      inline T query(int l,int r){
        int k=__lg(r-l+1);
        return op(s[k][l],s[k][r-(1<<k)+1]);
      }
  };
}

功能介绍

  • sparse_table<T,op> s(vector<T> a):对于存储 T 类型的 \(a\) 数组(std::vector)创建一个 ST 表,op 为你需要维护的操作(特别地,它需要满足消去律 \(\mathrm{op}(x,x)=x\);例如,\(\gcd\)\(\max\)\(\mathrm{and}\) 等都满足消去律),时间复杂度 \(O(n\log n)\),这里 \(n(1\le n\le 10^7)\)\(a\) 的大小;
  • T s.query(int l,int r):返回 \(\mathrm{op}_{i=l}^r a_i\) 的值,时间复杂度 \(O(1)\)

示例代码

P2412 查单词 by FFTotoro

树状数组(Fenwick Tree)

namespace IAOI_lib{
  template<typename T> class fenwick_tree{
    private:
      vector<T> t;
    public:
      fenwick_tree(int n=2e5){
        t.resize(n);
      }
      inline int lowbit(int x){
        return x&-x;
      }
      inline void add(int p,T d){
        t[p++]+=d;
        while((p+=lowbit(p))<=t.size())t[p-1]+=d;
      }
      inline T pre_sum(int p){
        T s=t[p++];
        while((p-=lowbit(p))>0)s+=t[p-1];
        return s;
      }
      inline T sum(int l,int r){
        return pre_sum(r)-(l?pre_sum(l-1):0);
      }
  };
}

功能介绍

  • fenwick_tree<T> t(int n):创建一个大小为 \(n(1\le n\le 10^8)\)、存储类型为 T 的树状数组,时间复杂度 \(O(n)\)
  • void t.add(int p,T d):向树状数组 \(t\) 中的第 \(p(0\le p<n)\) 个元素加上 \(d\),时间复杂度 \(O(\log n)\)
  • T t.sum(int l,int r):返回 \(\sum\limits_{i=l}^r a_i(0\le l\le r<n)\),时间复杂度 \(O(\log n)\)

示例代码

P3970 [TJOI2014] 上升子序列 by FFTotoro

网络最大流(Max Flow)

namespace IAOI_lib{
  template<typename C> class mf_graph{
    private:
      typedef pair<int,int> pii;
      int n;
      vector<vector<pii> > g;
      inline void add(int u,int v,C w){
        g[u].emplace_back(v,e.size());
        e.emplace_back(u,v,w,0);
      }
    public:
      struct edge{
        int u,v; C c,f;
        edge(int u,int v,C c,C f):u(u),v(v),c(c),f(f){}
      };
      vector<edge> e;
      mf_graph(int n):n(n),g(n){}
      inline void add_edge(int u,int v,C w){
        add(u,v,w),add(v,u,0);
      }
      edge get_edge(int x){return e[x<<1];}
      vector<edge> edges(){
        vector<edge> r;
        for(int i=0;i<e.size();i+=2)
          r.emplace_back(e[i]);
        return r;
      }
      C flow(int s,int t,C l=numeric_limits<C>::max()){
        C r=0;
        vector<int> d(n),p(n);
        auto dfs=[&](auto &&self,int u,C l)->C{
          if(u==t)return l;
          C r=0;
          for(int &i=p[u];i<g[u].size();i++){
            auto [v,x]=g[u][i];
            if(d[v]==d[u]+1&&e[x].c>e[x].f){
              C f=self(self,v,min(l-r,e[x].c-e[x].f));
              if(f){
                e[x].f+=f,e[x^1].f-=f,r+=f;
                if(r==l)return r;
              }
            }
          }
          return d[u]=-1,r;
        };
        while(1){
          fill(d.begin(),d.end(),-1),d[s]=0;
          queue<int> q; q.emplace(s);
          while(!q.empty()){
            int u=q.front(); q.pop();
            for(auto [i,x]:g[u])
              if(d[i]<0&&e[x].c>e[x].f)
                d[i]=d[u]+1,q.emplace(i);
          }
          if(d[t]<0)break;
          fill(p.begin(),p.end(),0);
          C f=dfs(dfs,s,l-r);
          if(!f)break;
          r+=f;
        }
        return r;
      }
  };
}

功能介绍

  • mf_graph<C> g(int n):创建一个流量类型为 C 的、具有 \(n\) 个结点的网络流图,时间复杂度 \(O(n)\)
  • void g.add_edge(int u,int v,C w):加入一条 \(u\to v\) 的、容量为 \(w\) 的边,时间复杂度 \(O(1)\)
  • C g.flow(int s,int t,C l):求源点为 \(s\)、汇点为 \(t\) 的网络最大流,流量上限为 \(l\)\(l\) 默认为类型 C 的最大值),时间复杂度 \(O(|V|^2|E|)\)
  • edge get_edge(int x):获取加入的第 \(x\) 条边的信息(流量),时间复杂度 \(O(1)\)
  • vector<edge> edges():获取所有边的信息(流量),时间复杂度 \(O(|E|)\)

示例代码

B3607 [图论与代数结构 502] 网络流_2 by FFTotoro

矩形面积并(Atlantis)

namespace IAOI_lib{
#define int long long
  class atlantis{
    typedef pair<int,int> pii;
    typedef tuple<int,int,int,int> tpi;
    private:
      struct line{
        int l,r,h,s;
        bool operator <(const line &x)const{
          return h<x.h;
        }
      };
      int n;
      vector<line> L;
      vector<pii> B;
      vector<int> X,C,S;
      void build(int u,int l,int r){
        if(B[u]=make_pair(l,r);l==r)return;
        int m=l+r>>1;
        build(u<<1,l,m),build(u<<1|1,m+1,r);
        pushup(u);
      }
      inline void pushup(int u){
        if(C[u])S[u]=X[B[u].second+1]-X[B[u].first];
        else S[u]=S[u<<1]+S[u<<1|1];
      }
      void update(int u,int l,int r,int c){
        if(X[B[u].second+1]<=l||r<=X[B[u].first])return;
        if(l<=X[B[u].first]&&X[B[u].second+1]<=r)C[u]+=c;
        else update(u<<1,l,r,c),update(u<<1|1,l,r,c);
        pushup(u);
      }
      int all_prod(){return S[1];}
    public:
      int areas_union(vector<tpi> a){
        X.resize(a.size()<<1),L.resize(a.size()<<1);
        for(int i=0;i<a.size();i++){
          auto &[xa,ya,xb,yb]=a[i];
          if(xa>xb)swap(xa,xb); if(ya>yb)swap(ya,yb);
          X[i<<1]=xa,X[i<<1|1]=xb;
          L[i<<1]=(line){xa,xb,ya,1},L[i<<1|1]=(line){xa,xb,yb,-1};
        }
        sort(L.begin(),L.end(),[](line x,line y){return x.h<y.h;});
        sort(X.begin(),X.end()),n=unique(X.begin(),X.end())-X.begin();
        B.resize(n<<2),C.resize(n<<2),S.resize(n<<3),build(1,0,n-2);
        int c=0;
        for(int i=0;i+1<a.size()<<1;i++){
          update(1,L[i].l,L[i].r,L[i].s);
          c+=all_prod()*(L[i+1].h-L[i].h);
        }
        return c;
      }
  };
#undef int
}

功能介绍

  • long long atlantis(vector<tuple<long long,long long,long long,long long> > a):求若干个四边平行于坐标轴的矩形的面积并。

示例代码

P10096 [ROIR 2023 Day 1] 扫地机器人 by FFTotoro

后缀数组(Suffix Array)

namespace IAOI_lib{
  namespace internal{
    template<typename T> vector<T> discretization(vector<T> a){
      auto b=a; sort(b.begin(),b.end());
      b.erase(unique(b.begin(),b.end()),b.end());
      for(auto &i:a)i=lower_bound(b.begin(),b.end(),i)-b.begin();
      return a;
    }
    vector<int> sa_doubling(vector<int> s){
      int n=s.size();
      vector<int> sa(n),rk=s;
      iota(sa.begin(),sa.end(),0);
      int v=*max_element(rk.begin(),rk.end())+1;
      for(int k=1;k<n&&v<n;k<<=1){
        auto cmp=[&](int x,int y){
          if(rk[x]!=rk[y])return rk[x]<rk[y];
          int rx=x+k<n?rk[x+k]:-1,ry=y+k<n?rk[y+k]:-1;
          return rx<ry;
        };
        vector<basic_string<int> > v1(v+1),v2(v);
        for(int i=0;i<n;i++)
          v1[i+k<n?rk[i+k]+1:0]+=i;
        for(int i=0;i<=v;i++)
          for(int p:v1[i])v2[rk[p]]+=p;
        for(int i=0,c=0;i<v;i++)
          for(int p:v2[i])sa[c++]=p;
        vector<int> r(n);
        for(int i=1;i<n;i++)
          r[sa[i]]=r[sa[i-1]]+cmp(sa[i-1],sa[i]);
        swap(r,rk),v=rk[sa[n-1]]+1;
      }
      return sa;
    }
  }
  template<typename T> vector<int> suffix_array(vector<T> s){
    s=internal::discretization<T>(s);
    vector<int> s2(s.begin(),s.end());
    return internal::sa_doubling(s2);
  }
  vector<int> suffix_array(string s){
    vector<char> s2(s.begin(),s.end());
    return suffix_array<char>(s2);
  }
  template<typename T> vector<int> lcp_array(vector<T> s,vector<int> sa){
    int n=s.size();
    vector<int> rk(n),lcp(n-1);
    for(int i=0;i<n;i++)
      rk[sa[i]]=i;
    for(int i=0,h=0;i<n-1;i++){
      if(h)h--;
      if(!rk[i])continue;
      int j=sa[rk[i]-1];
      while(i+h<n&&j+h<n&&s[i+h]==s[j+h])h++;
      lcp[rk[i]-1]=h;
    }
    return lcp;
  }
  vector<int> lcp_array(string s,vector<int> sa){
    vector<char> s2(s.begin(),s.end());
    return lcp_array<char>(s2,sa);
  }
}

功能介绍

  • vector<int> suffix_array<T>(vector<T> s):求出一个存储类型为 T 的数组 \(s\) 的后缀数组,时间复杂度 \(O(n\log n)\)
  • vector<int> suffix_array(string s):求出一个字符串 \(s\) 的后缀数组,时间复杂度 \(O(n\log n)\)
  • vector<int> lcp_array<T>(vector<T> s,vector<int> sa):求出一个存储类型为 T、后缀数组为 \(\text{sa}\) 的数组 \(s\)\(\text{lcp}\) 数组(又称 \(\text{height}\) 数组),时间复杂度 \(O(n)\)
  • vector<int> lcp_array(string s,vector<int> sa):求出一个后缀数组为 \(\text{sa}\) 的字符串 \(s\)\(\text{lcp}\) 数组,时间复杂度 \(O(n)\)

示例代码

P10469 后缀数组 by FFTotoro

数论(Number Theory)

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
namespace IAOI_lib{
  typedef long long ll;
#define st first
#define nd second
  vector<int> get_primes(int n){
    vector<bool> b(n+1);
    vector<int> p;
    for(int i=2;i<=n;i++){
      if(!b[i])p.emplace_back(i);
      for(int j:p){
        if(1ll*i*j>n)break;
        b[i*j]=true;
        if(!(i%j))break;
      }
    }
    return p;
  }
  inline ll safe_mulll(ll a,ll b,ll M){
    ll r=0;
    while(b){
      if(b&1)(r+=a)%=M;
      (a<<=1)%=M; b>>=1;
    }
    return r;
  }
  inline int pow_mod(int a,int b,int M){
    int r=1;
    while(b){
      if(b&1)r=r%M*a%M;
      a=a%M*a%M; b>>=1;
    }
    return r;
  }
  inline ll pow_modll(ll a,ll b,ll M){
    ll r=1;
    while(b){
      if(b&1)r=r%M*a%M;
      a=a%M*a%M; b>>=1;
    }
    return r;
  }
  inline int inv_p(int x,int M){
    return pow_mod(x,M-2,M);
  }
  inline ll inv_pll(ll x,ll M){
    return pow_modll(x,M-2,M);
  }
  pair<int,int> exgcd(int a,int b){
    if(!b)return make_pair(1,0);
    auto [x,y]=exgcd(b,a%b);
    int t=x; x=y; y=t-a/b*y;
    return make_pair(x,y);
  }
  pair<ll,ll> exgcdll(ll a,ll b){
    if(!b)return make_pair(1,0);
    auto [x,y]=exgcdll(b,a%b);
    ll t=x; x=y; y=t-a/b*y;
    return make_pair(x,y);
  }
  inline int inv_cp(int x,int M){
    if(gcd(x,M)>1)return -1;
    return (exgcd(x,M).st%M+M)%M;
  }
  inline ll inv_cpll(ll x,ll M){
    if(gcd(x,M)>1)return -1;
    return (exgcdll(x,M).st%M+M)%M;
  }
  inline ll crt(vector<pair<ll,ll> > a){
    ll p=1,s=0;
    for(auto [r,m]:a)p*=m;
    for(auto [r,m]:a){
      ll m2=p/m,i=inv_cpll(m2,m);
      s+=r*m2*i;
    }
    return s;
  }
  inline ll excrt(vector<pair<ll,ll> > e){
    auto [q,p]=e[0];
    for(int i=1;i<e.size();i++){
      auto [b,a]=e[i];
      auto [x,y]=exgcdll(p,a);
      ll d=gcd(p,a),c=b-q;
      if(c%d)return -1;
      if((x=(__int128)x*c/d%(a/d))<0)x+=a/d;
      ll m=lcm(p,a);
      if(q=(p*x+q)%m;q<0)q+=m;
      p=m;
    }
    return q%p;
  }
  inline long double lagrange(vector<pair<ll,ll> > a,ll k){
    long double c=0;
    for(int i=0;i<a.size();i++){
      long double s1=a[i].nd;
      for(int j=0;j<a.size();j++)
        if(i!=j)s1*=1.0*(k-a[j].st)/(a[i].st-a[j].st);
      c+=s1;
    }
    return c;
  }
  inline int lagrange_mod(vector<pair<int,int> > a,int k,int M){
    int c=0;
    for(int i=0;i<a.size();i++){
      int s1=a[i].nd,s2=1;
      for(int j=0;j<a.size();j++)
        if(i!=j)s1=1ll*s1*(k-a[j].st+M)%M,
          s2=1ll*s2*(a[i].st-a[j].st+M)%M;
      (c+=1ll*s1*inv_cpll(s2,M)%M)%=M;
    }
    return c;
  }
  inline int discrete_logarithm(int x,int y,int M){
    int b=sqrt(M),l=M/b+!!(M%b),r=M;
    vector<int> B(b+1),G(l+1);
    for(int i=B[0]=1;i<=b;i++)
      B[i]=1ll*B[i-1]*x%M;
    __gnu_pbds::gp_hash_table<int,pair<int,int> > t;
    for(int i=G[0]=1;i<=l;i++){
      G[i]=1ll*G[i-1]*B[b]%M;
      if(t.find(G[i])==t.end())t[G[i]].first=i;
      else if(!t[G[i]].second)t[G[i]].second=i;
    }
    auto check=[&](int k){return 1ll*G[k/b]*B[k%b]%M==y;};
    for(int i=0;i<=b;i++)
      if(int w=1ll*y*B[i]%M;t.find(w)!=t.end()){
        auto [r1,r2]=t[w];
        if(check(r1*b-i))r=min(r,r1*b-i);
        if(r2&&check(r2*b-i))r=min(r,r2*b-i);
      }
    return r==M?-1:r;
  }
#undef st
#undef nd
}

功能介绍

  • vector<int> get_primes(int n):查找 \([2,n](2\le n\le 10^8)\) 以内的所有质数,并返回包含它们的一个数组(std::vector),时间复杂度 \(O(n)\)
  • long long safe_mulll(long long a,long long b,long long M):返回 \(a\times b\bmod M(0\le a,b\le 10^{18},1\le M\le 10^{18})\) 的值,时间复杂度 \(O(\log b)\)
  • int pow_mod(int a,int b,int M) / long long pow_modll(long long a,long long n,long long M):返回 \(a^b\bmod M(0\le a,b\le 10^{9},1\le M\le 10^{9})\) 的值,时间复杂度 \(O(\log b)\)
  • int inv_p(int x,int M) / long long inv_pll(long long x,long long M):返回 \(x(1\le x<mod)\) 在模 \(M(1\le M\le 10^9)\)\(M\) 是质数)意义下的逆元,时间复杂度 \(O(\log M)\)
  • pair<int,int> exgcd(int a,int b) / pair<long long,long long> exgcdll(long long a,long long b):返回关于 \(x,y\) 的不定方程 \(ax+by=1\) 的解 \((x,y)\),时间复杂度 \(O(\log\max(a,b))\)
  • int inv_cp(int x,int M) / long long inv_cpll(long long x,long long M):返回 \(x(1\le x<M)\) 在模 \(M(1\le M\le 10^9)\)\(M\) 不一定是质数)意义下的逆元,如果 \(x\) 没有逆元返回 \(-1\),时间复杂度 \(O(\log M)\)
  • long long crt(vector<pair<long long,long long> > a) / long long crt_mod(vector<pair<long long,long long> > a,int M):返回满足 \(\forall1\le i,j\le n\land i\ne j,m_i\perp m_j\) 的方程组

    \[\begin{cases} x\equiv r_0\pmod {m_0}\\ x\equiv r_1\pmod {m_1}\\ \vdots\\ x\equiv r_{n-1}\pmod {m_{n-1}}\\ \end{cases} \]

    的最小非负整数解 \(x\),时间复杂度 \(O(n\log\max\{m_i\})\)。可选择是否将 \(x\) 对某个数 \(M\) 取模;
  • long double lagrange(vector<pair<long long,long long> > a,long long k):通过其在若干个点上的值确定一个多项式 \(f(x)\) 并求出 \(f(k)\),时间复杂度 \(O(n^2)\),这里 \(n\)\(a\) 的大小,即给出的点的个数;
  • int lagrange_mod(vector<pair<int,int> > a,int k,int M):同上,求出 \(f(k)\bmod M\),时间复杂度 \(O(n^2)\)
  • int discrete_logarithm(int x,int y,int M):返回最小的非负整数 \(k\) 满足 \(x^k\equiv y\pmod M(1\le M\le 10^9,\ 0\le x,y<M)\),如果不存在这样的 \(k\),返回 \(-1\),时间复杂度 \(O(\sqrt{M})\)

示例代码

P2480 古代猪文 by FFTotoro

第二部分 普通模板

警告:该部分模板未经过大量的实践测试,可能存在一定错误。请谨慎使用。

杂项(Misc)

离散化(Discretization)

namespace IAOI_lib{
  template<typename T> vector<T> discretization(vector<T> a){
    auto b=a; sort(b.begin(),b.end());
    b.erase(unique(b.begin(),b.end()),b.end());
    for(auto &i:a)i=lower_bound(b.begin(),b.end(),i)-b.begin();
    return a;
  }
}

线性代数(Linear Algebra)

矩阵(Matrix)

包含:矩阵转置(Transpose)、矩阵的秩(Rank)、行列式求值(Determinant)、线性方程组(System of Linear Equations)和普法夫型(Pfaffian)。

namespace IAOI_lib{
  namespace internal{
    inline int pow_mod(int a,int b,int p){
      int r=1;
      while(b){
        if(b&1)r=1ll*r*a%p;
        a=1ll*a*a%p,b>>=1;
      }
      return r;
    }
  }
  inline vector<vector<int> > trans(vector<vector<int> > a){
    vector b(a[0].size(),vector<int>(a.size()));
    for(int i=0;i<a.size();i++)
      for(int j=0;j<a[0].size();j++)
        b[j][i]=a[i][j];
    return b;
  }
  inline int rank(vector<vector<int> > a,const int p){
    auto self_add=[&](int &x,int y){
      if((x+=y)>=p)x-=p;
    };
    if(a.empty()||a[0].empty())return 0;
    if(a.size()>a[0].size())a=trans(a);
    int n=a.size(),m=a[0].size(),r=0;
    for(int c=0;c<m&&r<n;c++){
      int s=-1;
      for(int i=r;i<n;i++)
        if(a[i][c]){s=i; break;}
      if(s<0)continue;
      swap(a[r],a[s]);
      int x=internal::pow_mod(a[r][c],p-2,p);
      for(int i=c;i<m;i++)
        a[r][i]=1ll*a[r][i]*x%p;
      for(int i=0;i<n;i++)
        if(i!=r&&a[i][c])
          for(int j=c,w=a[i][c];j<m;j++)
            self_add(a[i][j],p-1ll*w*a[r][j]%p);
      r++;
    }
    return r;
  }
  inline int det(vector<vector<int> > a,const int p){
    auto self_add=[&](int &x,int y){
      if((x+=y)>=p)x-=p;
    };
    int n=a.size(),dt=1;
    for(int c=0;c<n;c++){
      int s=-1;
      for(int i=c;i<n;i++)
        if(a[i][c]){s=i; break;}
      if(s<0)return 0;
      if(c!=s)swap(a[c],a[s]),dt=p-dt;
      int x=internal::pow_mod(a[c][c],p-2,p);
      dt=1ll*dt*a[c][c]%p;
      for(int i=c;i<n;i++)
        a[c][i]=1ll*a[c][i]*x%p;
      for(int i=c+1;i<n;i++)
        if(a[i][c])
          for(int j=c,w=a[i][c];j<n;j++)
            self_add(a[i][j],p-1ll*w*a[c][j]%p);
    }
    return dt;
  }
  inline int det_arbitrary_mod(vector<vector<int> > a,const int p){
    auto self_add=[&](int &x,int y){
      if((x+=y)>=p)x-=p;
    };
    int n=a.size(),dt=1;
    for(int c=0;c<n;c++){
      int s=-1;
      for(int i=c;i<n;i++)
        if(a[i][c]){s=i; break;}
      if(s<0)return 0;
      if(c!=s)swap(a[c],a[s]),dt=p-dt;
      for(int i=c+1;i<n;i++){
        while(a[c][c]){
          int d=a[i][c]/a[c][c];
          for(int j=c;j<n;j++)
            self_add(a[i][j],p-1ll*a[c][j]*d%p);
          swap(a[i],a[c]),dt=p-dt;
        }
        swap(a[i],a[c]),dt=p-dt;
      }
    }
    for(int i=0;i<n;i++)
      dt=1ll*dt*a[i][i]%p;
    return dt;
  }
  pair<vector<int>,vector<vector<int> > > linear_equations(vector<vector<int> > A,vector<int> b,const int p){
    auto self_add=[&](int &x,int y){
      if((x+=y)>=p)x-=p;
    };
    int n=A.size(),m=A[0].size();
    vector<vector<int> > a(n,vector<int>(m+1));
    for(int i=0;i<n;i++)
      copy(A[i].begin(),A[i].end(),a[i].begin()),a[i][m]=b[i];
    vector<int> mp(m,-1);
    for(int c=0,r=0;c<m&&r<n;c++){
      for(int i=r;i<n;i++)
        if(a[i][c]){mp[c]=i; break;}
      if(mp[c]<0)continue;
      swap(a[mp[c]],a[r]),mp[c]=r;
      int x=internal::pow_mod(a[r][c],p-2,p);
      for(int i=c;i<=m;i++)
        a[r][i]=1ll*a[r][i]*x%p;
      for(int i=0;i<n;i++)
        if(i!=r&&a[i][c])
          for(int j=c,w=a[i][c];j<=m;j++)
            self_add(a[i][j],p-1ll*w*a[r][j]%p);
      r++;
    }
    vector<int> c(m,-1);
    for(int i=0;i<m;i++)
      c[i]=~mp[i]?a[mp[i]][m]:0;
    for(int i=0;i<n;i++){
      int s=0;
      for(int j=0;j<m;j++)
        self_add(s,1ll*a[i][j]*c[j]%p);
      if(s!=a[i][m])return make_pair(vector<int>(),vector<vector<int> >());
    }
    vector<vector<int> > bs;
    for(int i=0;i<m;i++)
      if(mp[i]<0){
        vector<int> d(m); d[i]=1;
        for(int j=0;j<m;j++)
          if(~mp[j])d[j]=(p-a[mp[j]][i])%p;
        bs.emplace_back(d);
      }
    return make_pair(c,bs);
  }
  inline int pfaffian(vector<vector<int> > a,const int p){
    auto self_add=[&](int &x,int y){
      if((x+=y)>=p)x-=p;
    };
    int n=a.size(),pf=1;
    for(int c=0;c<n;c+=2){
      int s=-1;
      for(int i=c;i<n;i++)
        if(a[i][c+1]){s=i; break;}
      if(s<0)return 0;
      if(c!=s){
        swap(a[c],a[s]);
        for(int i=0;i<n;i++)
          swap(a[i][c],a[i][s]);
        pf=p-pf;
      }
      int x=internal::pow_mod(a[c][c+1],p-2,p);
      pf=1ll*pf*a[c][c+1]%p;
      for(int i=c+1;i<n;i++)
        a[c][i]=1ll*a[c][i]*x%p,a[i][c]=1ll*a[i][c]*x%p;
      for(int j=c+2;j<n;j++)
        if(a[c+1][j])
          for(int i=c+1,w=a[c+1][j];i<n;i++)
            self_add(a[i][j],1ll*w*a[i][c]%p);
      for(int i=c+2;i<n;i++)
        if(a[i][c+1])
          for(int j=c+1,w=a[i][c+1];j<n;j++)
            self_add(a[i][j],p-1ll*w*a[c][j]%p);
    }
    return pf;
  }
}

异或线性基(XOR Basis)

namespace IAOI_lib{
  typedef long long ll;
  class linear_basis{
    private:
      vector<ll> a;
    public:
      linear_basis(int b){
        a.resize(b);
      }
      inline void insert(ll x){
        for(int i=a.size()-1;~i;i--)
          if(x>>i&1){
            if(a[i])x^=a[i];
            else{a[i]=x; break;}
          }
      }
      inline ll query(){
        ll c=0;
        for(int i=a.size()-1;~i;i--)
          c=max(c,c^a[i]);
        return c;
      }
      inline void merge(linear_basis x){
        assert(a.size()==x.a.size());
        for(int i=a.size()-1;~i;i--)
          this->insert(x.a[i]);
      }
  };
}

图论(Graph Theory)

2-满足性问题(2-SAT)

注意该模板输入为 \(1\)-indexed(按照 CNF 格式输入),返回值为 \(0\)-indexed。

namespace IAOI_lib{
  variant<vector<bool>,bool> twosat(int n,vector<pair<int,int> > e){
    vector<vector<int> > g(n<<1);
    for(auto &[u,v]:e){
      if(u<0)u=n-u; if(v<0)v=n-v;
      g[(u>n?u-n:u+n)-1].emplace_back(v-1);
      g[(v>n?v-n:v+n)-1].emplace_back(u-1);
    }
    int o=0,r=-1;
    vector<int> d(n<<1,-1),l(n<<1),c(n<<1,-1);
    stack<int> s;
    function<void(int)> tarjan=[&](int u){
      d[u]=l[u]=o++,s.emplace(u);
      for(int i:g[u])
        if(d[i]<0)tarjan(i),l[u]=min(l[u],l[i]);
        else if(c[i]<0)l[u]=min(l[u],d[i]);
      if(d[u]==l[u]){
        r++; while(s.top()!=u)
          c[s.top()]=r,s.pop();
        c[u]=r,s.pop();
      }
    };
    for(int i=0;i<n<<1;i++)
      if(d[i]<0)tarjan(i);
    for(int i=0;i<n;i++)
      if(c[i]==c[i+n])return false;
    vector<bool> b(n);
    for(int i=0;i<n;i++)
      b[i]=c[i]<c[i+n];
    return b;
  }
}

一般图最大匹配(Matching on General Graph)

namespace IAOI_lib{
  class matching{
    private:
      int n,c;
      vector<vector<int> > g;
      vector<int> o,p,pr,w,f;
      int leader(int x){
        return x==f[x]?x:f[x]=leader(f[x]);
      }
      inline int lca(int u,int v){
        c++,u=leader(u),v=leader(v);
        while(o[u]!=c){
          o[u]=c,u=leader(pr[p[u]]);
          if(v<n)swap(u,v);
        }
        return u;
      }
      void augment(int u){
        if(u<n)augment(p[pr[u]]),p[p[u]=pr[u]]=u;
      }
      bool blossom(int u){
        fill(pr.begin(),pr.end(),n);
        fill(w.begin(),w.end(),n);
        iota(f.begin(),f.end(),0);
        queue<int> q; q.emplace(u),w[u]=1;
        auto shrink=[&](int u,int v,int a){
          while(leader(u)!=a){
            pr[u]=v,v=p[u],f[u]=f[v]=a,u=pr[v];
            if(!w[v])w[v]=1,q.emplace(v);
          }
        };
        while(!q.empty()){
          int u=q.front(); q.pop();
          for(int i:g[u]){
            if(leader(u)==leader(i))continue;
            if(w[i]==1){
              int a=lca(u,i);
              shrink(u,i,a),shrink(i,u,a);
            }
            if(w[i]==n){
              pr[i]=u,w[i]=0;
              if(p[i]<n)w[p[i]]=1,q.emplace(p[i]);
              else{augment(i); return true;}
            }
          }
        }
        return false;
      }
    public:
      matching(int n_){
        c=0,g.resize(n=n_),p.resize(n+1,n);
        pr.resize(n+1),w.resize(n+1);
        o.resize(n+1),f.resize(n+1);
      }
      inline void add_edge(int u,int v){
        g[u].emplace_back(v);
        g[v].emplace_back(u);
      }
      pair<int,vector<pii> > solve(){
        int s=0; vector<pii> v;
        for(int i=0;i<n;i++)
          if(p[i]==n)s+=blossom(i);
        for(int i=0;i<n;i++)
          if(p[i]<i)v.emplace_back(p[i],i);
        return make_pair(s,v);
      }
  };
}

二分图边染色(Edge Coloring of Bipartite Graph)

#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
namespace IAOI_lib{
  class bipar_edge_coloring{
    private:
      vector<int> d;
      vector<vector<int> > a;
    public:
      bipar_edge_coloring(int n,int m){
        d.resize(n+m);
        a.resize(n+m,vector<int>(n+m,-1));
      }
      inline int add_edge(int u,int v){
        d[u]++,d[v]++;
        int c1=0,c2=0;
        while(~a[u][c1])c1++;
        while(~a[v][c2])c2++;
        a[u][c1]=v,a[v][c2]=u;
        if(c1!=c2)
          for(int c3=c2,i=v;~i;i=a[i][c3],c3^=c1^c2)
            swap(a[i][c1],a[i][c2]);
        return c1;
      }
      pair<int,vector<vector<int> > > color(){
        return make_pair(*max_element(d.begin(),d.end()),a);
      }
  };
  class associative_bipar_edge_coloring{
    private:
      vector<int> d;
      vector<__gnu_pbds::gp_hash_table<int,int> > a;
    public:
      associative_bipar_edge_coloring(int n,int m):d(n+m),a(n+m){}
      inline int add_edge(int u,int v){
        d[u]++,d[v]++;
        int c1=0,c2=0;
        while(a[u].find(c1)!=a[u].end())c1++;
        while(a[v].find(c2)!=a[v].end())c2++;
        if(a[u][c1]=v,a[v][c2]=u;c1!=c2)
          for(int c3=c2,i=v;~i;i=a[i].find(c3)!=a[i].end()?a[i][c3]:-1,c3^=c1^c2){
            bool f1=a[i].find(c1)==a[i].end(),f2=a[i].find(c2)==a[i].end();
            if(f1)a[i][c1]=a[i][c2],a[i].erase(c2);
            else if(f2)a[i][c2]=a[i][c1],a[i].erase(c1);
            else swap(a[i][c1],a[i][c2]);
          }
        return c1;
      }
      pair<int,vector<__gnu_pbds::gp_hash_table<int,int> > > color(){
        return make_pair(*max_element(d.begin(),d.end()),a);
      }
  };
}

最小费用最大流(Min Cost Flow)

namespace IAOI_lib{
  template<typename Cap,typename Cost> class mcf_graph{
    private:
      typedef tuple<int,Cost,int> tpi;
      typedef pair<Cost,int> pci;
      int n;
      vector<vector<tpi> > g;
      inline void add(int u,int v,Cap cap,Cost cost){
        g[u].emplace_back(v,cost,e.size());
        e.emplace_back(u,v,cap,0);
      }
    public:
      struct edge{
        int u,v; Cap c,f;
        edge(int u,int v,Cap c,Cap f):u(u),v(v),c(c),f(f){}
      };
      vector<edge> e;
      mcf_graph(int n):n(n),g(n){}
      inline void add_edge(int u,int v,Cap cap,Cost cost){
        add(u,v,cap,cost),add(v,u,0,-cost);
      }
      edge get_edge(int x){return e[x<<1];}
      vector<edge> edges(){
        vector<edge> r;
        for(int i=0;i<e.size();i+=2)
          r.emplace_back(e[i]);
        return r;
      }
      pair<Cap,Cost> flow(int s,int t,Cap l=numeric_limits<Cap>::max()){
        Cap r=0; Cost c=0;
        vector<int> p(n);
        vector<Cost> d(n);
        Cost lcost=numeric_limits<Cost>::max();
        vector<bool> b(n);
        auto dfs=[&](auto &&self,int u,Cap l)->Cap{
          if(u==t)return l;
          b[u]=true;
          Cap r=0;
          for(int &i=p[u];i<g[u].size();i++){
            auto [v,w,x]=g[u][i];
            if(!b[v]&&d[v]==d[u]+w&&e[x].c>e[x].f){
              Cap f=self(self,v,min(l-r,e[x].c-e[x].f));
              if(f){
                c+=f*w,e[x].f+=f,e[x^1].f-=f,r+=f;
                if(r==l)return b[u]=false,r;
              }
            }
          }
          return b[u]=false,r;
        };
        while(1){
          fill(d.begin(),d.end(),lcost),d[s]=0;
          vector<bool> inq(n);
          queue<int> q; inq[s]=true,q.emplace(s);
          while(!q.empty()){
            int u=q.front(); inq[u]=false,q.pop();
            for(auto [i,w,x]:g[u])
              if(d[i]>d[u]+w&&e[x].c>e[x].f)
                if(d[i]=d[u]+w;!inq[i])inq[i]=true,q.emplace(i);
          }
          if(d[t]==lcost)break;
          fill(p.begin(),p.end(),0);
          Cap f=dfs(dfs,s,l-r);
          if(!f)break;
          r+=f;
        }
        return make_pair(r,c);
      }
  };
}

无向图带权三元环计数(Enumerate Triangles)

namespace IAOI_lib{
  typedef long long ll;
  ll enumerate_triangles(vector<int> x,vector<vector<int> > g){
    int n=g.size(); ll c=0;
    vector<vector<int> > g2(n);
    for(int u=0;u<n;u++)
      for(int v:g[u])
        if(make_pair(g[u].size(),u)>make_pair(g[v].size(),v))
          g2[v].emplace_back(u);
    vector<int> b(n,-1);
    for(int u=0;u<n;u++){
      for(int v:g2[u])b[v]=u;
      for(int v:g2[u])
        for(int i:g2[v])
          if(b[i]==u)c+=1ll*x[u]*x[v]*x[i];
    }
    return c;
  }
  int enumerate_triangles_mod(vector<int> x,vector<vector<int> > g,const int p){
    auto add=[&](int &x,int y){
      if((x+=y)>=p)x-=p;
    };
    int n=g.size(),c=0;
    vector<vector<int> > g2(n);
    for(int u=0;u<n;u++)
      for(int v:g[u])
        if(make_pair(g[u].size(),u)>make_pair(g[v].size(),v))
          g2[v].emplace_back(u);
    vector<int> b(n,-1);
    for(int u=0;u<n;u++){
      for(int v:g2[u])b[v]=u;
      for(int v:g2[u])
        for(int i:g2[v])
          if(b[i]==u)add(c,1ll*x[u]*x[v]%p*x[i]%p);
    }
    return c;
  }
}

卷积

按位与 / 或 / 异或卷积(Bitwise AND / OR / XOR Convolution Mod \(998244353\)

namespace IAOI_lib{
  typedef long long ll;
  typedef vector<int> poly;
  const int p=998244353;
  namespace internal{
    inline int add(int x,int y){
      int s=x+y; if(s>=p)s-=p; return s;
    }
    inline void chadd(int &x,int y){
      if((x+=y)>=p)x-=p;
    }
    inline poly mul(poly a,poly b){
      poly s(a.size());
      for(int i=0;i<a.size();i++)
        s[i]=(ll)a[i]*b[i]%p;
      return s;
    }
    inline poly and_fwt(int n,poly a,int x){
      auto b=a; chadd(x,p);
      for(int i=0;i<n;i++)
        for(int j=0;j<a.size();j+=1<<i+1)
          for(int k=0;k<1<<i;k++)
            chadd(b[j|k],(ll)b[j|1<<i|k]*x%p);
      return b;
    }
    inline poly or_fwt(int n,poly a,int x){
      auto b=a; chadd(x,p);
      for(int i=0;i<n;i++)
        for(int j=0;j<a.size();j+=1<<i+1)
          for(int k=0;k<1<<i;k++)
            chadd(b[j|1<<i|k],(ll)b[j|k]*x%p);
      return b;
    }
    inline poly xor_fwt(int n,poly a,int x){
      auto b=a; chadd(x,p);
      for(int i=0;i<n;i++)
        for(int j=0;j<a.size();j+=1<<i+1)
          for(int k=0;k<1<<i;k++){
            int u=(ll)add(b[j|k],b[j|1<<i|k])*x%p,
              v=(ll)add(b[j|k],p-b[j|1<<i|k])*x%p;
            b[j|k]=u,b[j|1<<i|k]=v;
          }
      return b;
    }
  }
  inline poly and_convolution(poly a,poly b){
    int n=__lg(a.size());
    return internal::and_fwt(n,internal::mul(internal::and_fwt(n,a,1),internal::and_fwt(n,b,1)),-1);
  }
  inline poly or_convolution(poly a,poly b){
    int n=__lg(a.size());
    return internal::or_fwt(n,internal::mul(internal::or_fwt(n,a,1),internal::or_fwt(n,b,1)),-1);
  }
  inline poly xor_convolution(poly a,poly b){
    int n=__lg(a.size());
    return internal::xor_fwt(n,internal::mul(internal::xor_fwt(n,a,1),internal::xor_fwt(n,b,1)),p+1>>1);
  }
}

普通卷积(Convolution)

namespace IAOI_lib{
  typedef long long ll;
  const double PI=acos(-1);
  namespace internal{
    vector<int> t;
    inline void init(int n){
      int d=n>>1; t.clear();
      t.emplace_back(0),t.emplace_back(d);
      for(int w=2;w<=n;w<<=1){
        d>>=1;
        for(int p=0;p<w;p++) 
          t.emplace_back(t[p]|d);
      }
    }
    inline void fft(vector<complex<double> > &a,int n){
      for(int i=1;i<n;i++)
        if(t[i]>i)swap(a[i],a[t[i]]);
      for(int i=2;i<=n;i<<=1){
        complex<double> b(cos(PI/(i>>1)),sin(PI/(i>>1))),w(1,0);
        for(int l=0,r=i-1;r<=n;l+=i,r+=i){
          auto s=w;
          for(int p=l;p<l+(i>>1);p++){
            auto x=a[p]+s*a[p+(i>>1)],y=a[p]-s*a[p+(i>>1)];
            a[p]=x,a[p+(i>>1)]=y,s*=b;
          }
        }
      }
    }
  }
  inline vector<ll> convolution_ll(vector<int> x,vector<int> y){
    int l=1,n=x.size()+y.size();
    while(l<n<<1)l<<=1;
    while(x.size()<l)x.emplace_back(0);
    while(y.size()<l)y.emplace_back(0);
    internal::init(l);
    vector<complex<double> > a(x.begin(),x.end()),b(y.begin(),y.end()),c(l);
    internal::fft(a,l),internal::fft(b,l);
    for(int i=0;i<l;i++)
      c[i]=a[i]*b[i];
    internal::fft(c,l);
    reverse(c.begin()+1,c.end());
    vector<ll> s(n-1);
    for(int i=0;i<n-1;i++)
      s[i]=llround(c[i].real()/l);
    return s;
  }
}

普通卷积取模(Convolution Mod \(998244353\)

警告:使用 pow 函数时需要保证 \(x_0=1\)

namespace IAOI_lib{
  typedef long long ll;
  typedef vector<int> poly;
  const int g=3,ng=332748118,p=998244353;
  namespace internal{
    inline int add(int x,int y){
      int s=x+y; if(s>=p)s-=p; return s;
    }
    inline void chadd(int &x,int y){
      if((x+=y)>=p)x-=p;
    }
    inline int qpow(int a,int b){
      int r=1;
      while(b){
        if(b&1)r=1ll*r*a%p;
        a=1ll*a*a%p,b>>=1;
      }
      return r;
    }
    inline void ntt(poly &a,int l,int op){
      poly r(l);
      for(int i=1;i<l;i++)
        r[i]=(r[i>>1]>>1)|(i&1?l>>1:0);
      for(int i=0;i<l;i++)
        if(i<r[i])swap(a[i],a[r[i]]);
      for(int i=2;i<=l;i<<=1){
        int k=qpow(op>0?g:ng,(p-1)/i);
        vector<int> q(i>>1);
        for(int j=q[0]=1;j<i>>1;j++)
          q[j]=(ll)q[j-1]*k%p;
        for(int j=0;j<l;j+=i)
          for(int k=j;k<j+(i>>1);k++){
            int x=a[k],y=(ll)q[k-j]*a[k+(i>>1)]%p;
            chadd(a[k],y),a[k+(i>>1)]=add(x,p-y);
          }
      }
      if(op<0){
        int I=internal::qpow(l,p-2);
        for(int i=0;i<l;i++)
          a[i]=(ll)a[i]*I%p;
      }
    }
  }
  inline poly convolution(poly x,poly y){
    int l=1,n=x.size()+y.size();
    while(l<n<<1)l<<=1;
    while(x.size()<l)x.emplace_back(0);
    while(y.size()<l)y.emplace_back(0);
    internal::ntt(x,l,1),internal::ntt(y,l,1);
    for(int i=0;i<l;i++)
      x[i]=(ll)x[i]*y[i]%p;
    internal::ntt(x,l,-1);
    return poly(x.begin(),x.begin()+n-1);
  }
  inline poly operator *(const poly &x,const poly &y){
    return convolution(x,y);
  }
  inline poly operator *=(poly &x,const poly &y){
    return x=convolution(x,y);
  }
  inline poly pow(poly x,ll k){
    int n=x.size(),l=1;
    while(l<n<<1)l<<=1;
    while(x.size()<l)x.emplace_back(0);
    vector<int> r(l); r[0]=1;
    internal::ntt(r,l,1),internal::ntt(x,l,1);
    while(k){
      if(k&1){
        for(int i=0;i<l;i++)
          r[i]=(ll)r[i]*x[i]%p;
        internal::ntt(r,l,-1);
        fill(r.begin()+n,r.begin()+l,0);
        internal::ntt(r,l,1);
      }
      for(int i=0;i<l;i++)
        x[i]=(ll)x[i]*x[i]%p;
      internal::ntt(x,l,-1);
      fill(x.begin()+n,x.begin()+l,0);
      internal::ntt(x,l,1),k>>=1;
    }
    internal::ntt(r,l,-1);
    return poly(r.begin(),r.begin()+n);
  }
}

第三部分 命题相关模板

简单子任务配置生成器

#include<bits/stdc++.h>
using namespace std;
int main(){
  ios::sync_with_stdio(false);
  cout<<"Number of subtasks: ";
  int n; cin>>n;
  vector<int> a(n),s(n);
  for(int i=0;i<n;i++){
    cout<<"Number of testcases in subtask "<<i<<": ",cin>>a[i];
    cout<<"Score of subtask "<<i<<": ",cin>>s[i];
  }
  cout<<"Time Limit (ms): ";
  int tl; cin>>tl;
  cout<<"Memory Limit (MB): ";
  int ml; cin>>ml;
  freopen("config.yml","w",stdout);
  for(int i=0;i<n;i++){
    for(int j=0;j<a[i];j++){
      cout<<setfill('0')<<setw(2)<<i+1<<'-'<<setfill('0')<<setw(2)<<j+1<<".in: \n";
      cout<<"  timeLimit: "<<tl<<'\n';
      cout<<"  memoryLimit: "<<(ml<<10)<<'\n';
      cout<<"  score: "<<s[i]<<'\n';
      cout<<"  subtaskId: "<<i<<'\n'<<endl;
    }
  }
  return 0;
}
posted @ 2024-01-22 21:20  FFTotoro  阅读(200)  评论(0)    收藏  举报