算法板子

前言

这个板子跨时间较长,越后面的代码/封装的越好的代码越靠近本人最新的代码习惯,一些带 using namespace std; 的模板基本已经弃用

个人总结惯用模板

参考了很多人的模板,已经记不清了……就不写借自谁那里了……
目前已弃用的旧模板

#include <iostream>
#include <vector>
#include <algorithm>
#define dd(x) cout << #x << "=" << (x) << ", "
#define de(x) cout << #x << "=" << (x) << endl
#define rep(i, low, high) for(int i=(low); i<(high); ++i)
#define per(i, high, low) for(int i=(high)-1; i>=(low); --i)
using namespace std;
typedef pair<int,int> pii;
typedef long long i64;
typedef pair<i64,i64> pll;
typedef vector<int> vi;
typedef vector<i64> vi64;
/* If u think it all right but WA, check the problem limits. */

template<class A, class B> ostream& operator <<(ostream& out, const pair<A, B> &p) {return out << "(" << p.first << ", " << p.second << ")";}
template<class A> ostream& operator <<(ostream& out, const vector<A> &v) {out << "["; for(int i=0;i<v.size();++i) { if(i) out << ", "; out << v[i];	}	return out << "]";}

void sol() {

}

int main(int argc, char const *argv[])
{
  ios::sync_with_stdio(false);
  cin.tie(nullptr); cout.tie(nullptr);

  // freopen("in.txt","r",stdin);

  int t;
  cin >> t;
  for(;t;--t) {
    sol();
  }

  return 0;
}

新模板

#include <bits/stdc++.h>

int main() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);
}

对拍

Powershell

$file1=Get-Content out1.txt
$file2=Get-Content out2.txt
diff -ReferenceObject $file1 -DifferenceObject $file2 | Out-File -FilePath diff.txt -Encoding utf8

扩展欧几里得算法(From kuangbin)

求gcd

//返回 d=gcd(a,b); 和对应于等式 ax+by=d 中的 x,y
long long extend_gcd(long long a,long long b,long long &x,long long&y) {
  if(a==0&&b==0) return −1;//无最大公约数
  if(b==0) {x=1;y=0;return a;}
  long long d=extend_gcd(b,a%b,y,x);

  y−=a/b*x;
  return d;
}
  //********* 求逆元 *******************
  //ax = 1(mod n)
  long long mod_reverse(long long a,long long n) {
  long long x,y;
  long long d=extend_gcd(a,n,x,y);
  if(d==1) return (x%n+n)%n;
  else return −1;
}

简洁写法

注意:这个只能求 a < m 的情况,而且必须保证 a 和 m 互质

// 求 ax = 1( mod m) 的 x 值,就是逆元 (0<a<m)
long long inv(long long a,long long m) {
  if(a == 1)return 1;
  return inv(m%a,m)*(m−m/a)%m;
}

简洁dp写法

inv[0] = inv[1] = 1;
// i>=2 
inv[i] = mul(inv[mod%i], mod-mod/i); // mul表示相乘后取模

快速幂写法

\(O(log\ MOD)\),一般为30,不用递归,同样只能是质数

i64 mul(i64 x,i64 y) {
  return x*y%MOD;
}

i64 ksm(i64 base,i64 pow) {
  i64 now = base, ret=1;
  for(;pow;pow>>=1) {
    if(pow&1) {
      ret = mul(ret,now);
    }
    now = mul(now,now);
  }
  return ret;
}

i64 inv(i64 x) {
  return ksm(x,MOD-2);
}

随机搜索算法 (From QFYNH)

#include <ctime>

double start = clock();
int ans = 0x3f3f3f3f;
while((1.0 * clock() - start)/CLOCKS_PER_SEC <1.98) { // 卡时
  ... // 每次随机搜索前的预处理
  while(1) {
    if(...) break; // 搜索失败,退出
    if(...) { // 搜索成功,更新ans并退出
      ans = ...;
      break;
    }

    ... // 正文
  }
}

异或线性基(From Yveh)

// template
struct L_B{
  long long d[61],p[61];
  int cnt;
  L_B()
  {
    memset(d,0,sizeof(d));
    memset(p,0,sizeof(p));
    cnt=0;
  }
  bool insert(long long val)
  {
    for (int i=60;i>=0;i--)
      if (val&(1LL<<i))
      {
        if (!d[i])
        {
          d[i]=val;
          break;
        }
        val^=d[i];
      }
    return val>0;
  }
  long long query_max()
  {
    long long ret;
    for (int i=60;i>=0;i--)
      if ((ret^d[i])>ret)
        ret^=d[i];
    return ret;
  }
  long long query_min()
  {
    for (int i=0;i<=60;i++)
      if (d[i])
        return d[i];
    return 0;
  }
  long long query_min(long long x)
  {
    for (int i=60;i>=0;--i)
      if ((x>>i)&1)
        x = x^d[i];
    return x;
  }
  void rebuild()
  {
    for (int i=60;i>=0;i--)
      for (int j=i-1;j>=0;j--)
        if (d[i]&(1LL<<j))
          d[i]^=d[j];
    for (int i=0;i<=60;i++)
      if (d[i])
        p[cnt++]=d[i];
  }
  long long kthquery(long long k)
  {
    int ret=0;
    if (k>=(1LL<<cnt))
      return -1;
    for (int i=60;i>=0;i--)
      if (k&(1LL<<i))
        ret^=p[i];
    return ret;
  }
};
L_B merge(const L_B &n1,const L_B &n2)
{
  L_B ret=n1;
  for (int i=60;i>=0;i--)
    if (n2.d[i])
      ret.insert(n1.d[i]);
  return ret;
}

Dijkstra

void dij() {
  typedef struct _cmp
  {
    bool operator()(Node const&o1, Node const&o2) {
      return o1.cost>o2.cost;
    }
  }Cmp;
  priority_queue<Node,vector<Node>,Cmp> pq;
  pq.push(Node(0,-1,0,-1));
  while(!heap.empty()) {
    Node thiz = pq.top();
    thiz=pq.top();
    pq.pop();
    
    for(auto to : adj[thiz.v]) {
      if(to.to==thiz.f||to.to==0) continue;

      if(!dist[to.to]) {
        pq.push(Node(to.to,thiz.v,thiz.cost+to.w,to.id));
      }
    }
  }

  return;
}

三分搜索(单峰函数)

while(r - l >= threshold) {
  int ml = (l + r) / 2;
  int mr = ml + 1; // mr = (ml + r) / 2;
  int cal_ml = cal(ml), cal_mr = cal(mr);
  if(cal_ml < cal_mr) {
    res = mr;
    cal_res = cal_mr;
    l = ml;
  }
  else{
    res = ml;
    cal_res = cal_ml;
    r = mr;
  }
}

for(int i=l;i<=r;++i) {
  int cal_i = cal(i);
  if(cal_i >= cal_res) {
    res = i;
    cal_res = cal_i;
  }
}

二分搜索(单调函数)

while(l <= r) {
  int mid = l + r >> 1;
  if(check(mid)) {
    res = mid;
    r = mid - 1;
  }else {
    l = mid + 1;
  }
}

并查集

struct DSU {
    std::vector<int> f, siz;
    DSU(int n) : f(n), siz(n, 1) { std::iota(f.begin(), f.end(), 0); }
    int leader(int x) {
        while (x != f[x]) x = f[x] = f[f[x]];
        return x;
    }
    bool same(int x, int y) { return leader(x) == leader(y); }
    bool merge(int x, int y) {
        x = leader(x);
        y = leader(y);
        if (x == y) return false;
        siz[x] += siz[y];
        f[y] = x;
        return true;
    }
    int size(int x) { return siz[leader(x)]; }
};

Fenwick Tree (BIT)

注:有关取模等数论分块(cf-1553f)时,最好开2N的空间,不然容易WA(记得仔细考虑最后一块min(N, i*a[j]))
注2:碰上离散化时,请先考虑好要开多大
注3:碰上大数取模题,前缀和有可能溢出,请记得取模(如果和奇偶性有关,还要记得取两倍的模)【cf-1552f,1次RE,2次WA的痛……】

template<class T>
struct Fenwick{
    int n;
    std::vector<T> t;
    Fenwick(int n) : n(n + 1), t(n + 1) {}
 
    void isr(int p,T v) {
        for(++p;p<n;p+=p&-p) t[p] += v;
    }
 
    T qry(int p) {
        T res = 0;
        for(++p;p;p-=p&-p) res += t[p];
        return res;
    }
};

fhq_treap平衡树

#include <bits/stdc++.h>
using i64 = long long;

struct Info
{
    int randw;
    int val, sum;
    int sz;
    int ch[2];
    bool rev;

    Info(int val) : val(val), sz(1), sum(val), randw(rand()), rev(0) {
        ch[0] = ch[1] = 0;
    }
    Info() : val(0), sz(0), sum(0), randw(0), rev(0) {}
};


struct fhq_treap
{
    int n, cnt, root;
    std::vector<Info> info;

    fhq_treap(int n) : n(n), info(n+6), cnt(0), root(0) {
        srand(rand());
    }

    void pull(int x) {
        int &ch0 = info[x].ch[0];
        int &ch1 = info[x].ch[1];
        info[x].sz = info[ ch0 ].sz + info[ ch1 ].sz + 1;
        info[x].sum = info[ ch0 ].sum + info[ ch1 ].sum + info[x].val;
    }

    void insert(int val) {
        info[++cnt] = Info(val);
        root = merge(root, cnt);
    }

    void reverse(int l, int r) {
        int a,b,c;
        split(root, l, a, b);
        split(b, r-l, b, c);
        info[b].rev ^= 1;
        root = merge(a, merge(b,c));
    }

    void kill_rev(int x) {
        int &ch0 = info[x].ch[0];
        int &ch1 = info[x].ch[1];
        info[x].rev = 0;
        if(ch0) info[ch0].rev^=1;
        if(ch1) info[ch1].rev^=1;
        std::swap(ch0, ch1);
    }

    int merge(int x, int y) {
        if(!x || !y) return x+y;

        if(info[x].randw < info[y].randw) {
            if(info[x].rev) kill_rev(x);
            info[x].ch[1] = merge(info[x].ch[1], y);
            pull(x);
            return x;
        }else {
            if(info[y].rev) kill_rev(y);
            info[y].ch[0] = merge(x, info[y].ch[0]);
            pull(y);
            return y;
        }
    }

    void split(int thiz, int k, int &x, int &y) {
        if(!thiz) {
            x=y=0;
            return;
        }

        if(info[thiz].rev) kill_rev(thiz);

        if(info[ info[thiz].ch[0] ].sz < k) {
            x = thiz;
            split(info[thiz].ch[1], k-info[ info[x].ch[0] ].sz-1, info[thiz].ch[1], y);
        }else {
            y = thiz;
            split(info[thiz].ch[0], k, x, info[thiz].ch[0]);
        }
        pull(thiz);
    }

    void print(int thiz=-1) {
        if(thiz==-1) thiz = root;

        if(!thiz) return;

        if(info[thiz].rev) {
            kill_rev(thiz);
        }
        print(info[thiz].ch[0]);
        std::cout << info[thiz].val << " ";
        print(info[thiz].ch[1]);
    }
};

using namespace std;

int main(int argc, char const *argv[])
{
    std::ios_base::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);

    int n,m;
    cin >> n >> m;
    fhq_treap t(n);
    for(int i=1;i<=n;++i) {
        t.insert(i);
    }
    for(int i=0;i<m;++i) {
        int l,r;
        cin >> l >> r;
        --l;
        t.reverse(l,r);
    }
    t.print();
    cout << endl;

    return 0;
}

矩阵乘法(From JustinRochester)

Matrix operator * (const Matrix &x) const {
    Matrix y;
    for (int i = 0; i < 4; i++)
        for (int k = 0; k < 4; k++) {
            ll s = Num[i][k];
            for (int j = 0; j < 4; j++)
                y.Num[i][j] += s * x.Num[k][j];
        }
    return y;
}

莫队排序

普通分块

inline bool cmp(register query a,register query b) {return a.bl==b.bl?a.r<b.r:a.bl<b.bl;}

奇偶排序(更优)

inline bool cmp(register query a,register query b) {return a.bl!=b.bl?a.l<b.l:((a.bl&1)?a.r<b.r:a.r>b.r);}

pb_ds红黑树

#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;
using i64 = long long;

struct Node {
    i64 val;
    int id;

    Node(int v, int id=0) : val(v), id(id) {}

    friend bool operator<(const Node& o1, const Node& o2) {
        if(o1.val != o2.val) return o1.val < o2.val;
        else return o1.id < o2.id;
    }
};

int main(int argc, char const *argv[])
{
    tree<Node, null_type, less<Node>, rb_tree_tag, tree_order_statistics_node_update> t;

    ios_base::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);

    int n;
    cin >> n;
    for(int i=0;i<n;++i) {
        int cmd,x;
        cin >> cmd >> x;
        if(cmd==1) {
            t.insert(Node(x,i));
        }else if(cmd==2) {
            t.erase(t.lower_bound(Node(x)));
        }else {
            int ans;
            if(cmd==3) {
                ans = t.order_of_key(Node(x)) + 1;
            }else if(cmd==4) {
                ans = t.find_by_order(x-1)->val;
            }else if(cmd==5) {
                ans = (--t.lower_bound(Node(x)))->val;
            }else {
                ans = t.lower_bound(Node(x+1))->val;
            }
            cout << ans << endl;
        }
    }
    
    return 0;
}

欧拉筛莫比乌斯函数

// 建议多筛100个
void get_mob() {
    mob[1] = 1;
    for(int i=2;i<MX;++i) {
        if(!notP[i]) {
            prms[prm] = i;
            ++prm;
            mob[i] = -1;
        }
        for(int j=0;j<prm;++j) {
            if(i*prms[j]>=MX) break;

            mob[i*prms[j]] = -mob[i];
            notP[i*prms[j]] = true;

            if(i%prms[j]==0) {
                mob[i*prms[j]] = 0;
                break;
            }
        }
    }
}

筛欧拉函数

void get_phi() {
    for(int i=0;i<MX;++i) {
        ph[i] = i;
    }
    for(int i=2;i<MX;++i) {
        if(ph[i]==i)
            for(int j=i;j<MX;j+=i) {
                ph[j] -= ph[j]/i;
            }
    }
}

欧拉筛筛欧拉函数

namespace MF { // math function

constexpr int N = 1 << 23;
int phi[N], phi_pre[N];
int n, prm[N];
bool notP[N] = {false};

void get_phi() {
    phi[0] = phi_pre[0] = 0;
    phi[1] = phi_pre[1] = 1;
    n = 0;

    for(int i=2;i<N;++i) {
        if(!notP[i]) {
            phi[i] = i - 1;
            prm[n] = i;
            ++n;
        }

        for(int j = 0; i * prm[j] < N; ++j) {
            register int x = i * prm[j];
            notP[x] = true;

            if(i % prm[j] == 0) {
                phi[x] = phi[i] * prm[j];
                break;
            }else {
                phi[x] = phi[i] * (prm[j] - 1);
            }
        }
        phi_pre[i] = phi_pre[i - 1] + phi[i];
    }
}

}

备注:

欧拉筛可用以下公式,

其中 \(p\in Prime\)

\[\begin{cases} & \phi(i\cdot p) = \phi(i) \cdot p, & gcd(i,p)>1 \\ & \phi(i\cdot p) = \phi(i) \cdot \phi(p) = \phi(i)\cdot (p-1), & gcd(i,p)=1 \end{cases} \]

手写哈希表(比stl快很多)

template<typename tk, typename tv>
struct hash_map{
    int *mp,*nxt,nds;
    tk *key;
    tv *val;
    int N;

    hash_map(int n) : N(n<<1),nds(0) {
        mp = new int[N]; 
        val = new tv[N]; 
        nxt = new int[N]; 
        key = new tk[N];
        for(int i=0;i<N;++i) mp[i] = -1, nxt[i] = -1, val[i] = 0, key[i] = -1;
    } // 记得析构

    int hash(tk a) { // custom Hash
        a = (0x11451419+a) ^ (a<<15);
        a = (0xab2c1e2a^a) + (a>>7);
        a = (0xcf123456+a) ^ (a<<3);
        a = (0x2b897bac^a) + (a>>6);
        return (a%N+N)%N;
    }

    void isr(tk k,tv v) {
        int p = hash(k);
        if(mp[p]==-1) mp[p] = nds, key[nds] = k, val[nds] += v, ++nds;
        else {
            int now = mp[p];
            while(key[now]!=k&&nxt[now]!=-1) 
                now = nxt[now];
            if(key[now]!=k) {
                nxt[now] = nds;
                now = nxt[now];
                key[now] = k;
                val[nds] += v;
                ++nds;
            }else {
                val[now] += v;
            }
        }
    }
    tv qry(tk k) {
        int p = hash(k);
        if(mp[p]==-1) return 0;
        else {
            int now = mp[p];
            while(key[now]!=k&&nxt[now]!=-1) now = nxt[now];
            if(key[now]!=k) return 0;
            else return val[now];
        }
    }
};

很快的快读(int)

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++)
char buf[(1 << 22)], *p1 = buf, *p2 = buf;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}

MTT

拆系数高精FFT(5次dft)

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<b;++i)
using namespace std;
using ll = long long;
using db = double;

const int M = 1 << 17 << 1;
const db pi = acos(-1);

struct vir{
    db r, i;
    vir(db r = 0.0, db i = 0.0) : r(r), i(i){}
    void print() {printf("%f %f\n", r, i);}
    vir operator +(const vir &c) {return vir(r + c.r, i + c.i);}
    vir operator -(const vir &c) {return vir(r - c.r, i - c.i);}
    vir operator *(const vir &c) {return vir(r * c.r - i * c.i, r * c.i + i * c.r);}
}w[2][M];

int A[M],B[M],tmp[M];
vir F1[M],F2[M],G1[M];

ll qpow(ll b,int p,const int mod) {
    ll ret=1;
    for(;p;p>>=1,b=b*b%mod) if(p&1) ret=ret*b%mod;
    return ret;
}

struct FFT{
    int N, na, nb, rev[M];
    void fft(vir *a, int f){
        vir x, y;
        rep(i, 0, N) if (i < rev[i]) swap(a[i], a[rev[i]]);
        for (int i = 1; i < N; i <<= 1)
            for (int j = 0, t = N/(i<<1); j < N; j += i<<1)
                for (int k = 0, l = 0; k < i; k++, l += t) 
                    x = w[f][l] * a[j+k+i], y = a[j+k], a[j+k] = y+x, a[j+k+i] = y-x;
        if (f) rep(i, 0, N) a[i].i /= N;
    }
    void work(){
        int d = __builtin_ctz(N);
        rep(i, 0, N) {
            rev[i] = (rev[i>>1] >> 1) | ((i&1) << (d-1));
            w[1][i] = w[0][i] = vir(cos(2*pi*i/N), sin(2*pi*i/N));
            w[1][i].i = -w[1][i].i;
        }
    }    
    void doit(vir *a, int n, int *res){ // [0, na)
        for (N = 1; N < n+n-1; N <<= 1);
        rep(i, n, N) a[i] = vir(0, 0);
        work(), fft(a, 0);
        rep(i, 0, N) a[i] = a[i] * a[i];
        fft(a, 1);
        rep(i, 0, 2*n-1) res[i] = (a[i].i/2.0+0.5);
    }
    void MTT(int *a, int *b, int*ret, int na, int nb, int p) {
        // ret to ret[N]
        const int thres = 15;
        const int K = 1<<thres;
        for (N = 1; N < na + nb - 1; N <<= 1);
        rep(i, na, N) F1[i]=F2[i]=vir();
        rep(i, nb, N) G1[i]=vir();
        work();
        rep(i, 0, na) {
            F1[i].r = F2[i].r = a[i] >> thres;
            F1[i].i = a[i] & K-1; F2[i].i = -F1[i].i; }
        rep(i, 0 ,nb) {
            G1[i].r = b[i] >> thres;
            G1[i].i = b[i] & K-1; }
        fft(F1,0); fft(F2,0); fft(G1,0);
        rep(i,0,N) F1[i] = F1[i]*G1[i], F2[i] = F2[i]*G1[i];
        fft(F1,1); fft(F2,1);
        rep(i,0,na+nb-1) {
            vir F3 = F1[i] + F2[i];
            vir F4 = F1[i] - F2[i];
            ll x1 = (ll) (F3.r/2.0+0.1) % p;
            ll x2 = (ll) (F1[i].i+0.1) % p;
            ll x3 = (ll) (-F4.r/2.0+0.1) % p;
            ll ans = (x1 * K % p * K % p + x2 * K % p + x3 % p) % p;
            // cout << ans << ' ';
            ret[i] = ans;
        }
    }
    void MTT_inv(int*a,int*ret,int n,int p) {
        // fft.MTT_inv(a,b,min(1<<17,n<<1),998244353);
        if(n==1) {
            ret[0] = qpow(a[0],p-2,p);
            return;
        }

        MTT_inv(a,ret,n>>1,p);
        rep(i,0,n) tmp[i] = a[i];
        MTT(tmp,ret,tmp,n,n>>1,p);
        tmp[0] = (2-tmp[0]+p)%p;
        rep(i,1,n) tmp[i] = (p-tmp[i])%p;
        MTT(ret,tmp,ret,n>>1,n,p);
    }
}fft;

三模数NTT + 中国剩余定理

void MTT() { // luogu 1e5: 524ms
    const int m1=469762049, m2=998244353, m3=1004535809;
    ntt.P = 469762049;
    ntt.doit(F1,G1,n,m);
    ntt.P = 998244353;
    ntt.doit(F2,G2,n,m);
    ntt.P = 1004535809;
    ntt.doit(F3,G3,n,m);
    for(int i=0;i<n+m-1;++i) {
        int k1 = 1ll * (F2[i]-F1[i]+m2) % m2 * kpow(m1, m2-2, m2) % m2;
        ll F4 = F1[i] + 1ll * k1 * m1;
        int k4 = 1ll * (F3[i]-F4%m3+m3) % m3 * kpow(1ll*m1*m2%m3, m3-2, m3) % m3;
        F1[i] = (F4 % MOD + 1ll * k4 * m1 % MOD * m2 % MOD) % MOD;
    }
    for(int i=0;i<n+m-1;++i) cout << F1[i] << ' ';
}

MTT多项式求逆

void MTT_inv(int*a,int*ret,int n,int p) {
    // fft.MTT_inv(a,b,min(1<<17,n<<1),998244353);
    if(n==1) {
        ret[0] = qpow(a[0],p-2,p);
        return;
    }

    MTT_inv(a,ret,n>>1,p);
    rep(i,0,n) tmp[i] = a[i];
    MTT(tmp,ret,tmp,n,n>>1,p);
    tmp[0] = (2-tmp[0]+p)%p;
    rep(i,1,n) tmp[i] = (p-tmp[i])%p;
    MTT(ret,tmp,ret,n>>1,n,p);
}

Bernoulli Number and Power Sum

int A[M],B[M];
const int N = 65536<<1;
int fac[N],inv[N],ifac[N];
const int MOD = 1e9+7;
void Bernoulli() {
    // B_i = \frac{x}{e^x-1} * x!
    fac[0] = inv[0] = ifac[0] = 1;
    fac[1] = inv[1] = ifac[1] = 1;
    for(int i=2;i<N;++i) {
        fac[i]=1ll*i*fac[i-1]%MOD;
        inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
        ifac[i]=1ll*inv[i]*ifac[i-1]%MOD;
    }
    for(int i=0;i<N-1;++i) A[i] = ifac[i+1];
    fft.MTT_inv(A,B,N,MOD);
    for(int i=0;i<N;++i) B[i] = 1ll*B[i]*fac[i]%MOD;
}
int C(int n,int m) {
    return 1ll * fac[n] * ifac[m] % MOD * ifac[n-m] % MOD;
}
int power_sum(ll n,int k,const int MOD) {
    // return \sum _{i=1} ^n i^k;
    // 51nod1258 T==500 5e4: 1359ms(N==65536), 1625ms(N==65536<<1)
    // preprocess: 453ms(N==65536<<1)
    ++n; n%=MOD;
    int ans=0,now=n;
    for(int i=k;i>=0;--i)
        ans = (ans + 1ll*C(k+1,i)*B[i]%MOD *now%MOD) % MOD, now=1ll*now*n%MOD;
    ans = 1ll * ans * inv[k+1] % MOD;
    return ans;
}

拉格朗日插值(from KobeDuu)

可用于求1到t次方的 前n个正整数幂和 的和

// O(n)
ll per[N],suf[N],S[N];
ll Lagrange(ll f[],int n,ll k) {
    // ll ret = Lagrange(S,max_power,target));
    // S[i]==val --> f(i) = val --> (i, val)
    // need to get inv_fac[]
    // O(n), 0 <= i <= n
    if(k <= n) return f[k];
    per[0] = suf[n+1] = 1;
    for(int i = 0;i <= n; ++i) per[i+1] = (k-i)%MOD*per[i]%MOD;
    for(int i = n;i >= 0; --i) suf[i] = (k-i)%MOD*suf[i+1]%MOD;
    ll fk = 0;
    for(int i = 0;i <= n; ++i){
        ll tep = f[i]*per[i]%MOD*suf[i+1]%MOD*ifac[i]%MOD*ifac[n-i]%MOD;
        if((n-i)&1) fk = (fk-tep+MOD)%MOD;
        else fk = (fk+tep)%MOD; 
    }
    return fk;
}

数论分块

for(i64 l=1,r;l<=N;l=r+1) {
    r = min(N/(N/l),M/(M/l));
    r = min(r,N);
    ans += (N/l) * (M/l) * (mob_sum[r]-mob_sum[l-1]);
}

多项式

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define i64 long long
#define poly std::vector<int>
// dont visit a[m] when a.size() <= m
// (a = fastpow(c,n-m+1,m+1)).resize(m+1);
// i64 res = a[m] - b[m];
// (b = fastpow(d,n-m+1,m+1)).resize(m+1);

constexpr int MOD = 998244353;

namespace Poly { // remember to resize
    const int N = (1 << 21), g = 3;
    // 1 << 21: 70 MB, 1 << 18: 24 MB
    inline int power(int x, ll p) {
        int res = 1;
        for (; p; p >>= 1, x = (ll)x * x % MOD) 
            if (p & 1)
                res = (ll)res * x % MOD;
        return res;
    }
    inline int fix(const int x) { return x >= MOD ? x - MOD : x; }
    void dft(poly& A, int n) {
        static ull W[N << 1], *H[30], *las = W, mx = 0;
        for (; mx < n; mx++) {
            H[mx] = las;
            ull w = 1, wn = power(g, (MOD - 1) >> (mx + 1));
            for(int i=0;i<1<<mx+1;++i) *las++ = w, w = w * wn % MOD;
        }
        if (A.size() != (1 << n))
            A.resize(1 << n);
        static ull a[N];
        for (int i = 0, j = 0; i < (1 << n); ++i) {
            a[i] = A[j];
            for (int k = 1 << (n - 1); (j ^= k) < k; k >>= 1);
        }
        for (int k = 0, d = 1; k < n; k++, d <<= 1)
            for (int i = 0; i < (1 << n); i += (d << 1)) {
                ull *l = a + i, *r = a + i + d, *w = H[k], t;
                for (int j = 0; j < d; j++, l ++, r++) {
                    t = (*r) * (*w++) % MOD;
                    *r = *l + MOD - t, *l += t;
                }
            }
        for(int i=0;i<1<<n;++i) A[i] = a[i] % MOD;
    }
 
    void idft(poly &a, int n) {
        a.resize(1 << n), reverse(a.begin() + 1, a.end());
        dft(a, n);
        int inv = power(1 << n, MOD - 2);
        for(int i=0;i<1<<n;++i) a[i] = (ll)a[i] * inv % MOD;
    }
 
    poly FIX(poly a) {
        while (!a.empty() && !a.back()) a.pop_back();
        return a;
    }
 
    poly add(poly a, poly b, int op = 0) {
        a.resize(std::max(a.size(), b.size()));
        for(int i=0;i<b.size();++i) a[i] = fix(op ? a[i] + MOD - b[i] : a[i] + b[i]);
        return FIX(a);
    }

    // remember to resize
    poly mul(poly a, poly b, int t = 1) {
        if (t == 1 && a.size() + b.size() <= 24) {
            poly c(a.size() + b.size(), 0);
            for(int i=0;i<a.size();++i) for(int j=0;j<b.size();++j) c[i + j] = (c[i + j] + (ll)a[i] * b[j]) % MOD;
            return FIX(c);
        }
        int n = 1, aim = a.size() * t + b.size();
        while ((1<<n) <= aim) n++;
        dft(a, n); dft(b, n);
        if (t == 1)
            for(int i=0;i<1<<n;++i) a[i] = (ll) a[i] * b[i] % MOD;
        else
            for(int i=0;i<1<<n;++i) a[i] = (ll) a[i] * a[i] % MOD * b[i] % MOD;
        idft(a, n); a.resize(aim);
        return FIX(a);
    }
 
    poly mul(poly a, int b) {
        for(int i=0;i<a.size();++i) a[i] = (ll)a[i] * b % MOD;
        return FIX(a);
    }
 
    poly inv(poly a, int n) {
        a.resize(n);
        poly b;
        if (n == 1) {
            b.push_back(power(a[0], MOD - 2));
            return b;
        }
        b = inv(a, n + 1 >> 1);
        b = add(mul(b, 2), mul(b, a, 2), 1);
        return b.resize(n), b;
    }
 
    poly Der(poly a) {
        for(int i=0;i<a.size()-1;++i) a[i] = (ll)(i + 1) * a[i + 1] % MOD;
        return a.pop_back(), a;
    }
 
    poly Int(poly a) {
        static int inv[N];
        inv[1] = 1;
        a.push_back(0);
        for(int i=2;i<a.size();++i) inv[i] = (ll)(MOD - MOD / i) * inv[MOD % i] % MOD;
        for(int i=a.size()-1;i>=1;--i) a[i] = (ll)a[i - 1] * inv[i] % MOD;
        return a[0] = 0, a;
    }
 
    poly Ln(poly a, int n) {
        a = mul(Der(a), inv(a, n)); a.resize(n - 1);
        return FIX(Int(a));
    }
 
    poly Exp(poly a, int n) {
        a.resize(n);
        poly b, one(1, 1);
        if (n == 1)
            return one;
        b = Exp(a, n + 1 >> 1);
        b = mul(b, add(add(a, Ln(b, n), 1), one));
        return b.resize(n), b;
    }
 
    poly fastpow(poly a, ll k, int n) {
        a.resize(n); a = FIX(a);
        if (!a.size())
            return a;
        int st = 0, base = 0;
        while(!a[st]) ++st;
        if (st * k >= n)
            return a.resize(0), a;
        for(int i=0;i<a.size()-st;++i) a[i] = a[i + st];
        if (st)
            a.resize(a.size() - st);
        base = a[0];
        ll inv = power(base, MOD - 2);
        for(int i=0;i<a.size();++i) a[i] = a[i] * inv % MOD;
        a = FIX(Exp(mul(Ln(a, n), k % MOD), n));
        if (st) {
            reverse(a.begin(), a.end());
            a.resize(a.size() + st * k);
            reverse(a.begin(), a.end());
            a.resize(n);
            a = FIX(a);
        }
        base = power(base, k);
        for(int i=0;i<a.size();++i) a[i] = (ll)a[i] * base % MOD;
        return FIX(a);
    }

    int Merge(std::vector<poly>&a) { // return index
        // if TLE(but it's truly RE), get N larger
        std::priority_queue<std::pair<int,int> > H; // <-size, index>
        int n = a.size();
        for(int i=0;i<n;++i) {
            H.emplace(-a[i].size(), i);
        }

        while(H.size()>=2) {
            int o1 = H.top().second; H.pop();
            int o2 = H.top().second; H.pop();
            poly res = mul(a[o1], a[o2]);
            a[o1].clear(); a[o2].clear();
            for(int i=0;i<res.size();++i) a[o1].push_back(res[i]);
            H.emplace(-a[o1].size(), o1);
        }

        return H.top().second; // index
    }
}

from Jiangly

constexpr int P = 998244353;
using i64 = long long;
// assume -P <= x < 2P
int norm(int x) {
    if (x < 0) {
        x += P;
    }
    if (x >= P) {
        x -= P;
    }
    return x;
}
template<class T>
T power(T a, int b) {
    T res = 1;
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}
struct Z {
    int x;
    Z(int x = 0) : x(norm(x)) {}
    int val() const {
        return x;
    }
    Z operator-() const {
        return Z(norm(P - x));
    }
    Z inv() const {
        assert(x != 0);
        return power(*this, P - 2);
    }
    Z &operator*=(const Z &rhs) {
        x = i64(x) * rhs.x % P;
        return *this;
    }
    Z &operator+=(const Z &rhs) {
        x = norm(x + rhs.x);
        return *this;
    }
    Z &operator-=(const Z &rhs) {
        x = norm(x - rhs.x);
        return *this;
    }
    Z &operator/=(const Z &rhs) {
        return *this *= rhs.inv();
    }
    friend Z operator*(const Z &lhs, const Z &rhs) {
        Z res = lhs;
        res *= rhs;
        return res;
    }
    friend Z operator+(const Z &lhs, const Z &rhs) {
        Z res = lhs;
        res += rhs;
        return res;
    }
    friend Z operator-(const Z &lhs, const Z &rhs) {
        Z res = lhs;
        res -= rhs;
        return res;
    }
    friend Z operator/(const Z &lhs, const Z &rhs) {
        Z res = lhs;
        res /= rhs;
        return res;
    }
};
 
std::vector<int> rev;
std::vector<Z> roots{0, 1};
void dft(std::vector<Z> &a) {
    int n = a.size();
    
    if (int(rev.size()) != n) {
        int k = __builtin_ctz(n) - 1;
        rev.resize(n);
        for (int i = 0; i < n; i++) {
            rev[i] = rev[i >> 1] >> 1 | (i & 1) << k;
        }
    }
    
    for (int i = 0; i < n; i++) {
        if (rev[i] < i) {
            std::swap(a[i], a[rev[i]]);
        }
    }
    if (int(roots.size()) < n) {
        int k = __builtin_ctz(roots.size());
        roots.resize(n);
        while ((1 << k) < n) {
            Z e = power(Z(3), (P - 1) >> (k + 1));
            for (int i = 1 << (k - 1); i < (1 << k); i++) {
                roots[2 * i] = roots[i];
                roots[2 * i + 1] = roots[i] * e;
            }
            k++;
        }
    }
    for (int k = 1; k < n; k *= 2) {
        for (int i = 0; i < n; i += 2 * k) {
            for (int j = 0; j < k; j++) {
                Z u = a[i + j];
                Z v = a[i + j + k] * roots[k + j];
                a[i + j] = u + v;
                a[i + j + k] = u - v;
            }
        }
    }
}
void idft(std::vector<Z> &a) {
    int n = a.size();
    std::reverse(a.begin() + 1, a.end());
    dft(a);
    Z inv = (1 - P) / n;
    for (int i = 0; i < n; i++) {
        a[i] *= inv;
    }
}
struct Poly {
    std::vector<Z> a;
    Poly() {}
    Poly(const std::vector<Z> &a) : a(a) {}
    int size() const {
        return a.size();
    }
    void resize(int n) {
        a.resize(n);
    }
    Z operator[](int idx) const {
        if (idx < 0 || idx >= size()) {
            return 0;
        }
        return a[idx];
    }
    Z &operator[](int idx) {
        return a[idx];
    }
    Poly mulxk(int k) const {
        auto b = a;
        b.insert(b.begin(), k, 0);
        return Poly(b);
    }
    Poly modxk(int k) const {
        k = std::min(k, size());
        return Poly(std::vector<Z>(a.begin(), a.begin() + k));
    }
    Poly divxk(int k) const {
        if (size() <= k) {
            return Poly();
        }
        return Poly(std::vector<Z>(a.begin() + k, a.end()));
    }
    friend Poly operator+(const Poly &a, const Poly &b) {
        std::vector<Z> res(std::max(a.size(), b.size()));
        for (int i = 0; i < int(res.size()); i++) {
            res[i] = a[i] + b[i];
        }
        return Poly(res);
    }
    friend Poly operator-(const Poly &a, const Poly &b) {
        std::vector<Z> res(std::max(a.size(), b.size()));
        for (int i = 0; i < int(res.size()); i++) {
            res[i] = a[i] - b[i];
        }
        return Poly(res);
    }
    friend Poly operator*(Poly a, Poly b) {
        if (a.size() == 0 || b.size() == 0) {
            return Poly();
        }
        int sz = 1, tot = a.size() + b.size() - 1;
        while (sz < tot)
            sz *= 2;
        a.a.resize(sz);
        b.a.resize(sz);
        dft(a.a);
        dft(b.a);
        for (int i = 0; i < sz; ++i) {
            a.a[i] = a[i] * b[i];
        }
        idft(a.a);
        a.resize(tot);
        return a;
    }
    friend Poly operator*(Z a, Poly b) {
        for (int i = 0; i < int(b.size()); i++) {
            b[i] *= a;
        }
        return b;
    }
    friend Poly operator*(Poly a, Z b) {
        for (int i = 0; i < int(a.size()); i++) {
            a[i] *= b;
        }
        return a;
    }
    Poly &operator+=(Poly b) {
        return (*this) = (*this) + b;
    }
    Poly &operator-=(Poly b) {
        return (*this) = (*this) - b;
    }
    Poly &operator*=(Poly b) {
        return (*this) = (*this) * b;
    }
    Poly deriv() const {
        if (a.empty()) {
            return Poly();
        }
        std::vector<Z> res(size() - 1);
        for (int i = 0; i < size() - 1; ++i) {
            res[i] = (i + 1) * a[i + 1];
        }
        return Poly(res);
    }
    Poly integr() const {
        std::vector<Z> res(size() + 1);
        for (int i = 0; i < size(); ++i) {
            res[i + 1] = a[i] / (i + 1);
        }
        return Poly(res);
    }
    Poly inv(int m) const {
        Poly x({a[0].inv()});
        int k = 1;
        while (k < m) {
            k *= 2;
            x = (x * (Poly({2}) - modxk(k) * x)).modxk(k);
        }
        return x.modxk(m);
    }
    Poly log(int m) const {
        return (deriv() * inv(m)).integr().modxk(m);
    }
    Poly exp(int m) const {
        Poly x({1});
        int k = 1;
        while (k < m) {
            k *= 2;
            x = (x * (Poly({1}) - x.log(k) + modxk(k))).modxk(k);
        }
        return x.modxk(m);
    }
    Poly sqrt(int m) const {
        Poly x({1});
        int k = 1;
        while (k < m) {
            k *= 2;
            x = (x + (modxk(k) * x.inv(k)).modxk(k)) * ((P + 1) / 2);
        }
        return x.modxk(m);
    }
    Poly mulT(Poly b) const {
        if (b.size() == 0) {
            return Poly();
        }
        int n = b.size();
        std::reverse(b.a.begin(), b.a.end());
        return ((*this) * b).divxk(n - 1);
    }
    std::vector<Z> eval(std::vector<Z> x) const {
        if (size() == 0) {
            return std::vector<Z>(x.size(), 0);
        }
        const int n = std::max(int(x.size()), size());
        std::vector<Poly> q(4 * n);
        std::vector<Z> ans(x.size());
        x.resize(n);
        std::function<void(int, int, int)> build = [&](int p, int l, int r) {
            if (r - l == 1) {
                q[p] = Poly({1, -x[l]});
            } else {
                int m = (l + r) / 2;
                build(2 * p, l, m);
                build(2 * p + 1, m, r);
                q[p] = q[2 * p] * q[2 * p + 1];
            }
        };
        build(1, 0, n);
        std::function<void(int, int, int, const Poly &)> work = [&](int p, int l, int r, const Poly &num) {
            if (r - l == 1) {
                if (l < int(ans.size())) {
                    ans[l] = num[0];
                }
            } else {
                int m = (l + r) / 2;
                work(2 * p, l, m, num.mulT(q[2 * p + 1]).modxk(m - l));
                work(2 * p + 1, m, r, num.mulT(q[2 * p]).modxk(r - m));
            }
        };
        work(1, 0, n, mulT(q[1].inv(n)));
        return ans;
    }
};

KMP

\(O(n)\)求前i个字符的前缀函数(最长的真前缀真后缀匹配长度)

std::vector<int> pi(m);
for(int i=1;i<m;++i) {
    int j = pi[i-1];
    while(j>0 && t[i]!=t[j])
        j = pi[j-1];
    if(t[i]==t[j])
        pi[i] = j + 1;
    else pi[i] = 0;
}

ex_kmp / z function

\(O(n)\)求i开始的后缀与原串的LCP

int k = t.size();
std::vector<int> z(k); // z[0] = 0;
for(int i=1,l=0,r=0;i<k;++i) {
    if(i<=r && z[i-l] < r-i+1) {
        z[i] = z[i-l];
    }else {
        z[i] = std::max(0, r-i+1);
        while(i+z[i]<k && t[i+z[i]]==t[z[i]]) ++z[i];
    }

    if(i+z[i]-1 > r) {
        l = i;
        r = i + z[i] - 1;
    }
}

AC automaton

求多模式匹配,建造模式串trie树,吃文本串

查询模式串出现次数: \(O((\sum |s|)^2)\)

查询模式串是否出现:\(O(\sum |s|)\)

namespace ACautomaton {

const int SIGMA = 26;

int tr[TOT][SIGMA], tot = 1;
int fail[TOT], end[TOT];
int cnt[N];
int max;

void init() {
    // 多组输入才需要初始化
    tot = 1;
    max = 0;
    for(int i=0;i<TOT;++i) {
        // 一般清除到最长串即可
        for(int j=0;j<SIGMA;++j) {
            tr[i][j] = 0;
        }
        fail[i] = 0;
        end[i] = 0;
    }
    for(int i=0;i<N;++i) {
        cnt[i] = 0;
    }
}

void insert(const std::string &s,const int id) {
    int u = 0, n = s.size();
    for(int i=0;i<n;++i) {
        if(!tr[u][s[i] - 'a']) {
            tr[u][s[i] - 'a'] = tot;
            ++tot;
        }
        u = tr[u][s[i] - 'a'];
    }
    end[u] = id;
}

void build() {
    std::queue<int> q;
    for(int i=0;i<SIGMA;++i) {
        if(tr[0][i]) {
            q.emplace(tr[0][i]);
        }
    }
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        for(int i=0;i<SIGMA;++i) {
            if(tr[u][i]) {
                fail[tr[u][i]] = tr[fail[u]][i];
                q.emplace(tr[u][i]);
            }else {
                tr[u][i] = tr[fail[u]][i];
            }
        }
    }
}

void query(const std::string&t, std::vector<int>&res) {
    int u = 0, n = t.size();
    for(int i=0;i<n;++i) {
        u = tr[u][t[i] - 'a'];
        // 计数所有模式串出现次数
        for(int j=u;j;j=fail[j]) {
            if(end[j]) {
                ++cnt[end[j]];
                max = std::max(max, cnt[end[j]]);
            }
        }
    }

/* 查询模式串是否出现过
        for(int j=u;j && end[j]!=-1;j=fail[j]) {
            res += end[j];
            end[j] = -1;
        }
*/
    
    for(int i=1;i<N;++i) {
        if(cnt[i] == max) {
            res.emplace_back(i);
        }
    }
}

}

Suffix Array (from kuang_bin)

#include <bits/stdc++.h>

// template from kuangbin

/*
* suffix array
* 倍增算法 O(n*logn)
* 待排序数组长度为 n, 放在 0 n-1 中,在最后面补一个 0
* da(str ,sa,rank,height, n , m);//注意是 n, m是str[i]的最大值
* 例如:
* n = 8;
* num[] = { 1, 1, 2, 1, 1, 1, 1, 2, $ }; 注意 num 最后一位为 0,其他大于 0
* rank[] = 4, 6, 8, 1, 2, 3, 5, 7, 0 ;rank[0~n-1] 为有效值,rank[n]必定为 0 无效值
* sa[] = 8, 3, 4, 5, 0, 6, 1, 7, 2 ;sa[1~n] 为有效值,sa[0] 必定为 n 是无效值
* height[]= 0, 0, 3, 2, 3, 1, 2, 0, 1 ;height[2~n] 为有效值
* 用于记录排序后相邻后缀的LCP
*/
const int MAXN = 1e2 + 6;
int t1[MAXN], t2[MAXN], c[MAXN]; // 求 SA 数组需要的中间变量,不需要赋值
// 待排序的字符串放在 s 数组中,从 s[0] 到 s[n-1], 长度为 n, 且最大值小于 m,
// 除 s[n-1] 外的所有 s[i] 都大于 0,r[n-1]=0
// 函数结束以后结果放在 sa 数组中
bool cmp(int *r, int a, int b, int l) {
    return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int str[], int sa[], int rank[], int height[], int n, int m) {
    n++;
    int i, j, p, *x = t1, *y = t2;
    //第一轮基数排序,如果 s 的最大值很大,可改为快速排序
    for (i = 0; i < m; i++) c[i] = 0;
    for (i = 0; i < n; i++) c[x[i] = str[i]]++;
    for (i = 1; i < m; i++) c[i] += c[i-1];
    for (i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;
    for (j = 1; j <= n; j <<= 1) {
        p = 0;
        //直接利用 sa 数组排序第二关键字
        for (i = n-j; i < n; i++)
            y[p++] = i; //后面的 j 个数第二关键字为空的最小
        for (i = 0; i < n; i++)
            if (sa[i] >= j)
                y[p++] = sa[i] - j;
        //这样数组 y 保存的就是按照第二关键字排序的结果
        //基数排序第一关键字
        for (i = 0; i < m; i++) c[i] = 0;
        for (i = 0; i < n; i++) c[x[y[i]]]++;
        for (i = 1; i < m; i++) c[i] += c[i-1];
        for (i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
        //根据 sa 和 x 数组计算新的 x 数组
        std::swap(x, y);
        p = 1;
        x[sa[0]] = 0;
        for (i = 1; i < n; i++)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
        if (p >= n) break;
        m = p; //下次基数排序的最大值
    }
    int k = 0;
    n--;
    for (i = 0; i <= n; i++)
        rank[sa[i]] = i;
    for (i = 0; i < n; i++) {
        if (k) k--;
        j = sa[rank[i]-1];
        while (str[i + k] == str[j + k]) k++;
        height[rank[i]] = k;
    }
}

std::string s;
int r[MAXN], sa[MAXN], rank[MAXN], height[MAXN];

int main(int argc, char const *argv[]) {
    std::cin >> s;
    int n = s.size();
    for(int i=0;i<n;++i) r[i] = s[i];
    // r表示原来的数组,注意要传入int型
    // sa表示排第i名的后缀的起点
    // rank表示以i为起点的后缀的排名
    // height表示与前一个后缀的LCP
    da(r,sa,rank,height,n,128);
    for(int i=1;i<=n;++i) std::cout << sa[i] + 1 << ' ';
}

动态开点线段树 (From Jiangly)

namespace SegTree {

struct Info {
    int v, x;
    Info(int v=0,int x=-1) : x(x), v(v) {}
    bool operator>(const Info &o) const {
        if(v == o.v) return x < o.x;
        else return v > o.v;
    }
};

Info max(const Info x, const Info y) {
    if(x > y) return x;
    else return y;
}

Info merge(const Info &a, const Info &b) {
    return max(a, b);
}

struct Node {
    Node *l,*r;
    Info val;
    Node() : l(nullptr), r(nullptr), val() {}
};

void pull(Node *&p) {
    p->val = merge(p->l == nullptr ? Info() : p->l->val, p->r == nullptr ? Info() : p->r->val);
}

// query(t, 0, n, query_left, query_right)
Info query(Node *t, int l, int r, int le, int ri) { // [le, ri)
    //   l    r
    // ooxxxxxooo
    // L R LR L R
    // xxooxooxxo
    if(ri <= l || r <= le || t == nullptr) {
        return Info();
    }
    if(le <= l && r <= ri) {
        return t->val;
    }
    int mid = l + r >> 1;
    return merge(query(t->l, l, mid, le, ri), query(t->r, mid, r, le, ri));
}

void modify(Node *&t, int l, int r, int x, int v) { // [l,r)
    if(t == nullptr) {
        t = new Node();
    }
    if(r - l == 1) {
        t->val.v += v;
        t->val.x = x;
        return;
    }

    int mid = l + r >> 1;
    if(x < mid) {
        modify(t->l, l, mid, x, v);
    }else {
        modify(t->r, mid, r, x, v);
    }
    pull(t);
}

Node* merge(Node *&t, Node *&o, int l, int r) { // [l,r)
    if(t == nullptr) return o;
    else if(o == nullptr) return t;

    Node *p = new Node();

    if(r - l == 1) {
        p->val = t->val;
        p->val.v += o->val.v;
        return p;
    }

    int mid = l + r >> 1;
    p->l = merge(t->l, o->l, l, mid);
    p->r = merge(t->r, o->r, mid, r);
    pull(p);
    return p;
}

}

Suffix Automaton

// luogu 1e6 max total extend time: 142ms
namespace SAM {

const int TOT = 1e6 + 6;
const int MAXLEN = TOT * 2;
const int SIGMA = 26;

struct State {
    int len, link;
    // len: 子串长度, link: fail指针
    // std::map<char, int> next;
    int next[SIGMA];
};

// SAM
State st[MAXLEN];
// bcbcbc, bcbc算出现2次
int tot = 0;
int last = 0;

void init() {
    // 不调用会跑死循环
    st[0].len = 0;
    st[0].link = -1;
    tot = 1;
    last = 0;
}

int extend(int c, int last) {
    // maybe int, char
    c -= 'a'; // if using map, delete this
    int cur = tot, p;
    ++tot;
    st[cur].len = st[last].len + 1;
    for(p = last; p != -1 && !st[p].next[c]; p = st[p].link) {
        st[p].next[c] = cur;
    }

    if(p == -1) {
        st[cur].link = 0;
    }else {
        int t = st[p].next[c];
        if(st[p].len + 1 == st[t].len) {
            st[cur].link = t;
        }else {
            int clone = tot;
            ++tot;
            st[clone].len = st[p].len + 1;
            for(int i=0;i<SIGMA;++i) {
                st[clone].next[i] = st[t].next[i];
            }
            st[clone].link = st[t].link;
            for(;p!=-1 && st[p].next[c] == t;p = st[p].link) {
                st[p].next[c] = clone;
            }
            st[t].link = st[cur].link = clone;
        }
    }
    return cur;
}

void extend(int c) {
    last = extend(c, last);
}

}

广义后缀自动机

离线 / Trie 树做法

// luogu 1e6 max time: 406ms
// 离线做法
namespace TSAM { // Trie_SAM
// 0为tire树根,tot为节点总数目[0,tot)
// 当广义后缀自动机建立后,通常字典树结构将会被破坏

const int TOT = 1e6 + 6;
const int MAXLEN = TOT * 2;
const int SIGMA = 26;

struct State {
    int len, link;
    int next[SIGMA];
};

// SAM
State st[MAXLEN];
int tot = 0;
// Trie
int tr[TOT][SIGMA], cnt = 1;
int fa[TOT], pos[TOT], ch[TOT];

void insert(const std::string &s) {
    int u = 0, n = s.size();
    for(int i=0;i<n;++i) {
        int &nxt = tr[u][s[i] - 'a'];
        if(!nxt) {
            nxt = cnt;
            ch[nxt] = s[i];
            fa[nxt] = u;
            ++cnt;
        }
        u = nxt;
    }
}

void SAM_init() {
    // 不调用会跑死循环
    st[0].len = 0;
    st[0].link = -1;
    tot = 1;
}

int extend(int c, int last) {
    c -= 'a';
    int cur = tot, p;
    ++tot;
    st[cur].len = st[last].len + 1;
    for(p = last; p != -1 && !st[p].next[c]; p = st[p].link) {
        st[p].next[c] = cur;
    }

    if(p == -1) {
        st[cur].link = 0;
    }else {
        int t = st[p].next[c];
        if(st[p].len + 1 == st[t].len) {
            st[cur].link = t;
        }else {
            int clone = tot;
            ++tot;
            st[clone].len = st[p].len + 1;
            for(int i=0;i<SIGMA;++i) {
                st[clone].next[i] = st[t].next[i];
            }
            st[clone].link = st[t].link;
            for(;p!=-1 && st[p].next[c] == t;p = st[p].link) {
                st[p].next[c] = clone;
            }
            st[t].link = st[cur].link = clone;
        }
    }
    return cur;
}

void build() {
    // 请先吃入多模式串来构建trie树后再调用该函数构建Trie上SAM
    std::queue<int> q;
    for(int i=0;i<SIGMA;++i) if(tr[0][i]) q.push(tr[0][i]);
    pos[0] = 0;
    while(!q.empty()) {
        int x = q.front(); q.pop();
        pos[x] = extend(ch[x], pos[fa[x]]);
        for(int i=0;i<SIGMA;++i) if(tr[x][i]) {
            q.push(tr[x][i]);
        }
    }
}

i64 query() {
    // 本质不同子串个数
    i64 res = 0;
    for(int i=1;i<tot;++i) {
        res += st[i].len - st[st[i].link].len;
    }
    return res;
}

}

在线做法

// luogu 1e6 max time: 203ms
// 在线做法
namespace GSAM { // online general SAM

const int TOT = 1e6 + 6;
const int MAXLEN = TOT * 2;
const int SIGMA = 26;

struct State {
    int len, link;
    // len: 子串长度, link: fail指针
    // std::map<char, int> next;
    int next[SIGMA];
};

// SAM
State st[MAXLEN];
int tot = 0;
int last = 0;

void init() {
    // 不调用会跑死循环
    for(int i=0;i<tot;++i) {
        siz[i] = 0;
        st[i].len = 0;
        st[i].link = 0;
        for(int c = 0; c < SIGMA; ++c) {
            st[i].next[c] = 0;
        }
    }
    st[0].len = 0;
    st[0].link = -1;
    tot = 1;
    last = 0;
}

int make_clone(int p,int c) {
    int t = st[p].next[c];
    int clone = tot;
    ++tot;
    st[clone].len = st[p].len + 1;
    for(int i=0;i<SIGMA;++i) {
        st[clone].next[i] = st[t].next[i];
    }
    st[clone].link = st[t].link;
    for(; p != -1 && st[p].next[c] == t; p = st[p].link) {
        st[p].next[c] = clone;
    }
    return clone;
}

// last 表示以c为结尾的前缀节点
// 分不清可以写盗版(删去节点已存在部分)
// 多串的size要分开记录,不能揉成一坨
int extend(int c, int last) {
    // maybe int, char
    c -= 'a'; // if using map, delete this

    // 节点已存在
    if(st[last].next[c]) {
        int p = last, t = st[last].next[c];
        int ret = t;
        if(st[p].len + 1 != st[t].len) {
            // t节点已建立,仅需建立clone节点
            ret = st[t].link = make_clone(p,c);
        }
        return ret;
    }

    int cur = tot, p;
    ++tot;
    st[cur].len = st[last].len + 1;
    for(p = last; p != -1 && !st[p].next[c]; p = st[p].link) {
        st[p].next[c] = cur;
    }

    if(p == -1) {
        st[cur].link = 0;
    }else {
        int t = st[p].next[c];
        if(st[p].len + 1 == st[t].len) {
            st[cur].link = t;
        }else {
            st[t].link = st[cur].link = make_clone(p,c);
        }
    }
    return cur;
}

void extend(int c) {
    last = extend(c, last);
}

void finish() {
    // 串读完后重置last指针
    last = 0;
}

/*i64 query() { // query两串相同子串数
    // 需要extend return时往每个last指针所指节点sz+=1
    std::vector<std::vector<int> > g(tot);
    for(int i=1;i<tot;++i) {
        g[st[i].link].push_back(i);
    }

    std::function<void(int)> dfs = [&](int v) {
        for(int to : g[v]) {
            dfs(to);
            siz[v][0] += siz[to][0];
            siz[v][1] += siz[to][1];
        }
    };

    dfs(0);
    i64 res = 0;
    for(int i=1;i<tot;++i) {
        res += 1ll * siz[i][0] * siz[i][1] * (st[i].len - st[st[i].link].len);
    }
    return res;
}*/

}

/* example
// insert
GSAM::init();
while(q--) {
    std::cin >> s;
    for(int i=0;i<s.length();++i) {
        GSAM::extend(s[i]);
    }
    GSAM::finish();
}
*/

树上倍增

// isr
pth[0][id] = par[id];
for(int i=1;i<LOG;++i) {
    pth[i][id] = pth[i-1][pth[i-1][id]];
}

// qry
for(int i=LOG-1;i>=0;--i) {
    if(a[pth[i][now]]>0) {
        // rt 0 0 0 0 1 1 1 1
        // 需要往离根远的地方跳(1的连续段)
        // 否则,显然必定跳到根
        now = pth[i][now];
    }
}

高维前缀和

逐维计算前缀 \(O(k \cdot n^k)\)

for(every demension) {
    for(every poiot) {
        point[][][] += point[][now_demension - 1][];
    }
}

容斥 \(O(n^k \cdot 2*k)\)

加上2k-1维毗邻,减去2k维毗邻的前缀和,适用2-3维(好写),询问同理(询问只能容斥询问)

for(every point) {
    point[][][][][] += point[][now_demension - 1][][][];
    point[][][][][] -= point[][w - 1][x - 1][y - 1][z - 1];
    ... and so on
}

牛顿二项式定理与生成函数

\[\frac{1}{(1-x)^m} = \sum^\infty_{n=0} \binom{m+n-1}{n} x ^ n = \sum^\infty_{n=0} \binom{m+n-1}{m-1} x ^ n \]

k阶前缀/差分(NTT快速幂)

\[\Big(\big( (a \cdot b) \cdot b \big) \cdots \Big) \cdot b = a \cdot b^k \]

前缀和:\(b = \frac{1}{1 - x}\)
差分:\(b = 1 - x\)

set维护区间覆盖信息

只能维护当前一段是否有被覆盖,而不能维护是被什么覆盖

类似于一个bitset,只能维护01信息

\(O(n\ log\ n)\) 常数很小,大概是线段树的1/6

不需要离散化,如果要离散化请多开一倍,以判断是否毗邻

std::vector<int> vx(4 * n);
vx[i << 2] = a[i].x1;
vx[i<<2|1] = a[i].x2;
vx[i<<2|2] = a[i].x1 - 1; // 判断毗邻
vx[i<<2|3] = a[i].x2 + 1;
struct Rg { // range set
    using pii = std::pair<int,int>;
    const int INF = 0x3f3f3f3f;
    std::set<pii> LR;
    Rg() { LR.emplace(-2*INF, -INF); }
 
    bool insert(int l, int r) { // [l, r]
        auto it = LR.lower_bound({l, INF});
        --it;
        int L, R;
        if(it->first <= l && r <= it->second) {
            return false;
        }// else { return true; } // <--- query(int l, int r)
 
        if(l - 1 <= it->second) {
            L = it->first;
            R = std::max(it->second, r);
            LR.erase(it);
        }else {
            L = l;
            R = r;
        }
 
        auto jt = LR.lower_bound({L, INF});
        while(jt != LR.end() && L <= jt->first && jt->second <= R) {
            LR.erase(jt);
            jt = LR.lower_bound({L, INF});
        }
        if(jt != LR.end() && jt->first - 1 <= R) {
            R = jt->second;
            LR.erase(jt);
        }
 
        LR.emplace(L, R);
 
        return true;
    }
};

线段树维护子区段最大值

struct Info {
    i64 ls,rs,sum,v;
    Info(i64 x = 0) {
        if(x > 0) {
            ls = rs = sum = v = x;
        }else {
            sum = x, ls = rs = v = 0;
        }
    }
    friend Info merge(Info x,Info y) {
        Info res;
        res.ls = std::max(x.ls, x.sum + y.ls);
        res.rs = std::max(x.rs + y.sum, y.rs);
        res.v = std::max(std::max(x.v, y.v), x.rs + y.ls);
        return res;
    }
};

网格化

// 适用于距离较近的点会被删除或较少的情况
// 可查询周围 3x3 格内的点
struct Grid {
    using T1 = int;
    T1 l;
    std::map<std::pair<int,int>, std::set<int> > mp;
 
    Grid(int l) : l(l) {}
 
    void insert(int x, int y, int idx) {
        mp[pii(x / l, y / l)].insert(idx);
    }
 
    int query(int x,int y,int res[],const std::vector<int> &isDis) {
        int n = 0;
        x /= l; y /= l;
        for(int dx : {-1, 0, 1}) {
            for(int dy : {-1, 0, 1}) {
                if(mp.count(pii(x + dx, y + dy)) == 0) continue;
 
                auto &v = mp[pii(x + dx, y + dy)];
                for(auto it = v.begin(); it != v.end();) {
                    if(isDis[*it] == true) {
                        auto jt = it;
                        ++it;
                        v.erase(jt);
                    }else {
                        res[n] = *it;
                        ++n;
                        ++it;
                    }
                }
            }
        }
        return n;
    }
};

Heavy-Light Decomposition

struct HLD {
    int n; // [0, n - 1]
    int *fa,*dep,*dfn,*siz,*hson,*top,*id;
    int cnt;
    std::vector<int> *g;
    HLD(int n,std::vector<int> g[],int rt) : n(n),g(g),cnt(0) {
        fa = new int[n];
        dep = new int[n];
        dfn = new int[n];
        siz = new int[n];
        hson = new int[n];
        top = new int[n];
        id = new int[n];
        dep[rt] = 0;
        dfs1(rt,-1);
        dfs2(rt,rt);
    } // 记得析构
 
    void dfs1(int v,int f) {
        hson[v] = -1;
        siz[v] = 1;
        fa[v] = f;
        for(int to : g[v]) if(to != f) {
            dep[to] = dep[v] + 1;
            dfs1(to, v);
            siz[v] += siz[to];
            if(hson[v] == -1 || siz[to] > siz[hson[v]]) hson[v] = to;
        }
    }
 
    void dfs2(int v,int t) {
        top[v] = t;
        dfn[v] = cnt;
        id[cnt] = v;
        ++cnt;
        if(hson[v] == -1) return;
        dfs2(hson[v], t);
        for(int to : g[v]) if(to != fa[v] && to != hson[v]) {
            dfs2(to, to);
        }
    }
 
    int lca(int u,int v) {
        while(top[u] != top[v]) {
            if(dep[top[u]] > dep[top[v]]) u = fa[top[u]];
            else v = fa[top[v]];
        }
        return dep[u] < dep[v] ? u : v;
    }

    void path(int x,int y,std::function<void(int,int)> cb) {
        while(top[x] != top[y]) {
            if(dep[top[x]] < dep[top[y]]) {
                std::swap(x, y);
            }
            cb(dfn[top[x]], dfn[x]);
            x = fa[top[x]];
        }
        cb(std::min(dfn[x], dfn[y]), std::max(dfn[x], dfn[y]));
    }
};

// st 是线段树结构体
int querymax(int x, int y) {
  int ret = -inf, fx = top[x], fy = top[y];
  while (fx != fy) {
    if (dep[fx] >= dep[fy])
      ret = max(ret, st.query1(1, 1, n, dfn[fx], dfn[x])), x = fa[fx];
    else
      ret = max(ret, st.query1(1, 1, n, dfn[fy], dfn[y])), y = fa[fy];
    fx = top[x];
    fy = top[y];
  }
  if (dfn[x] < dfn[y])
    ret = max(ret, st.query1(1, 1, n, dfn[x], dfn[y]));
  else
    ret = max(ret, st.query1(1, 1, n, dfn[y], dfn[x]));
  return ret;
}

主席树

查询区间众数(严格大于1/2次出现,否则-1)

普通版

struct Info {
    int val;
    Info(int v=0) : val(v) {} 
    friend Info merge(const Info a,const Info b) {
        return Info(a.val + b.val);
    }
};

struct Stable_SegTree {
    int n;
    // std::vector<int> t;
    Info *t;
    int *ls,*rs;
    int tot;
    Stable_SegTree(int n,int m) : n(n),tot(0) {
        int LOG = 20;
        int TOT = n * 4 + m * LOG + 6;
        // 没空间限制就尽量开多点
        t = new Info[TOT];
        ls = new int[TOT];
        rs = new int[TOT];
        build(0,n);
    }
    ~Stable_SegTree() { // if MLE, usually multi test case
        delete t;
        delete ls;
        delete rs;
    }

    int build(int l,int r) {
        int p = tot;
        ++tot;
        t[p].val = 0;
        int mid = l + r >> 1;
        if(r - l != 1) {
            ls[p] = build(l,mid);
            rs[p] = build(mid,r);
        }
        return p;
    }

    void pull(int p) {
        t[p] = merge(t[ls[p]], t[rs[p]]);
    }

    int add(int l,int r,int bro,int x) {
        int p = ++tot;
        ls[p] = ls[bro];
        rs[p] = rs[bro];
        if(r - l == 1) {
            t[p] = Info(t[bro].val + 1);
            return p;
        }
        int mid = l + r >> 1;
        if(x < mid) {
            ls[p] = add(l, mid, ls[bro], x);
        }else {
            rs[p] = add(mid, r, rs[bro], x);
        }
        pull(p);
        return p;
    }
    int add(int bro,int x) {
        return add(0,n,bro,x);
    }

    // 树上差分式查询
    int query(int l,int r,int pu,int pv,int pa,int pfa,int m) {
        Info iu = t[pu];
        Info iv = t[pv];
        Info ia = Info(-t[pa].val);
        Info ifa = Info(-t[pfa].val);
        Info thiz = merge(iu, merge(iv, merge(ia, ifa)));
        if((iu.val + iv.val + ia.val + ifa.val) * 2 <= m) {
            return -1;
        }

        if(r - l == 1) {
            return l;
        }

        int mid = l + r >> 1;
        
        return std::max(query(l,mid,ls[pu],ls[pv],ls[pa],ls[pfa],m), query(mid,r,rs[pu],rs[pv],rs[pa],rs[pfa],m));
    }
    int query(int pu,int pv,int pa,int pfa,int m) {
        return query(0,n,pu,pv,pa,pfa,m);
    }
};

非指针动态开点版

struct Stable_SegTree {
    int n;
    // std::vector<int> t;
    Info *t;
    int *ls,*rs;
    int tot;
    Stable_SegTree(int n,int m) : n(n),tot(0) {
        int LOG = 20;
        int TOT = m * LOG + 6;
        t = new Info[TOT];
        ls = new int[TOT];
        rs = new int[TOT];
    }

    void pull(int p) {
        if(ls[p] == -1) t[p] = t[rs[p]];
        else if(rs[p] == -1) t[p] = t[ls[p]];
        else t[p] = merge(t[ls[p]], t[rs[p]]);
    }

    int add(int l,int r,int bro,int x) {
        int p = ++tot;
        ls[p] = bro == -1 ? -1 : ls[bro];
        rs[p] = bro == -1 ? -1 : rs[bro];
        if(r - l == 1) {
            t[p] = bro == -1 ? Info(1) : Info(t[bro].val + 1);
            return p;
        }
        int mid = l + r >> 1;
        if(x < mid) {
            ls[p] = add(l, mid, bro == -1 ? -1 : ls[bro], x);
        }else {
            rs[p] = add(mid, r, bro == -1 ? -1 : rs[bro], x);
        }
        pull(p);
        return p;
    }

    int query(int l,int r,int pu,int pv,int pa,int pfa,int m) {
        Info iu = pu == -1 ? Info() : t[pu];
        Info iv = pv == -1 ? Info() : t[pv];
        Info ia = pa == -1 ? Info() : Info(-t[pa].val);
        Info ifa = pfa == -1 ? Info() : Info(-t[pfa].val);
        Info thiz = merge(iu, merge(iv, merge(ia, ifa)));
        if((iu.val + iv.val + ia.val + ifa.val) * 2 <= m) {
            return -1;
        }

        if(r - l == 1) {
            return l;
        }

        int mid = l + r >> 1;
        int lsu,rsu,lsv,rsv,lsa,rsa,lsfa,rsfa;
        if(pu == -1) lsu = rsu = -1;
        else lsu = ls[pu], rsu = rs[pu];
        if(pv == -1) lsv = rsv = -1;
        else lsv = ls[pv], rsv = rs[pv];
        if(pa == -1) lsa = rsa = -1;
        else lsa = ls[pa], rsa = rs[pa];
        if(pfa == -1) lsfa = rsfa = -1;
        else lsfa = ls[pfa], rsfa = rs[pfa];
        
        return std::max(query(l,mid,lsu,lsv,lsa,lsfa,m), query(mid,r,rsu,rsv,rsa,rsfa,m));
    }
};

李超树

询问线段中单点最大值

// luogu 1e5: 60ms
using pdi = std::pair<double,int>;
constexpr double eps = 1e-8;
constexpr int INF = 0x3f3f3f3f;
struct Line {
    double k, b;
    int id;
    Line() : k(0),b(-INF),id(-1) {}
    Line(int x0,int y0,int x1,int y1,int id) : id(id) {
        if(x0 == x1) {
            k = 0, b = std::max(y0, y1);
            return;
        }else if(x0 > x1) {
            std::swap(x0,x1), std::swap(y0,y1);
        }
        k = 1.0 * (y1 - y0) / (x1 - x0);
        b = y0 - x0 * k;
    }
    double cal(int x) {
        return k * x + b;
    }
};


int sgn(double x) {
    if(fabs(x) <= eps) return 0;
    else if(x > 0) return 1;
    else return -1;
}

pdi merge(pdi x,pdi y) {
    int s = sgn(x.first - y.first);
    if(s > 0) return x;
    else if(s < 0) return y;
    else return pdi(x.first, std::min(x.second, y.second));
}

struct LiChaoTree {
    int n;
    std::vector<Line> t;
    LiChaoTree(int n) : n(n),t(4*n) {}

    void modify(int p,int l,int r,int L,int R,Line v) {
        // [L,R)
        int mid = l + r >> 1;
        if(R <= l || r <= L) {
            return;
        }
        if(L <= l && r <= R) {
            Line &u = t[p];
            if(sgn(u.cal(mid) - v.cal(mid)) < 0) std::swap(u,v); // u.mid >= v.mid
            if(sgn(u.cal(l) - v.cal(l)) < 0) modify(p<<1,l,mid,L,R,v);
            if(sgn(u.cal(r-1) - v.cal(r-1)) < 0) modify(p<<1|1,mid,r,L,R,v);
            return;
        }

        modify(p<<1,l,mid,L,R,v);
        modify(p<<1|1,mid,r,L,R,v);
    }
    void modify(int L,int R,Line v) {
        modify(1,0,n,L,R,v);
    }

    pdi query(int p,int l,int r,int x) {
        int mid = l + r >> 1;
        double thiz = t[p].cal(x);
        if(r - l == 1) return pdi(thiz, t[p].id);
        else if(x < mid) return merge(pdi(thiz, t[p].id), query(p<<1,l,mid,x));
        else return merge(pdi(thiz, t[p].id), query(p<<1|1,mid,r,x));
        
    }
    pdi query(int x) {
        return query(1,0,n,x);
    }
};

可持久化李超树

离散化,询问单点最小值

std::vector<int> v;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3f;
struct Line {
    i64 k, b;
    Line() : k(0),b(INF) {}
    Line(i64 k,i64 b) : b(b),k(k) {}
    i64 cal(int x) {
        return k * v[x] + b; // 离散化后结果
    }
};

int sgn(i64 x) {
    if(x == 0) return 0;
    else return x > 0 ? 1 : -1;
}

struct Stable_LiChaoTree {
    int n,tot;
    std::vector<Line> t;
    std::vector<int> ls,rs;
    
    Stable_LiChaoTree(int n,int m) : n(n),tot(0),t(m),ls(m,-1),rs(m,-1) {
        build(0,n);
    }

    void copy(int p,int o) {
        t[p] = t[o];
        ls[p] = ls[o];
        rs[p] = rs[o];
    }

    int build(int l,int r) {
        int p = tot;
        ++tot;
        if(r - l != 1) {
            int mid = l + r >> 1;
            ls[p] = build(l,mid);
            rs[p] = build(mid,r);
        }
        return p;
    }

    int modify(int bro,int l,int r,int L,int R,Line v) {
        if(R <= l || r <= L) {
            return bro;
        }
        // [L,R)
        int p = tot;
        if(p == 23) {
            int x = 1;
        }
        ++tot;
        copy(p,bro);
        int mid = l + r >> 1;
        if(L <= l && r <= R) {
            Line &u = t[p];
            if(sgn(u.cal(mid) - v.cal(mid)) > 0) std::swap(u,v); // u.mid <= v.mid
            if(sgn(u.cal(l) - v.cal(l)) > 0) ls[p] = modify(ls[bro],l,mid,L,R,v);
            if(sgn(u.cal(r-1) - v.cal(r-1)) > 0) rs[p] = modify(rs[bro],mid,r,L,R,v);
            return p;
        }

        ls[p] = modify(ls[bro],l,mid,L,R,v);
        rs[p] = modify(rs[bro],mid,r,L,R,v);
        return p;
    }
    int modify(int bro,int L,int R,Line v) {
        return modify(bro,0,n,L,R,v);
    }

    i64 query(int p,int l,int r,int x) {
        int mid = l + r >> 1;
        i64 thiz = t[p].cal(x);
        if(r - l == 1) return thiz;
        else if(x < mid) return std::min(thiz, query(ls[p],l,mid,x));
        else return std::min(thiz, query(rs[p],mid,r,x));
    }
    i64 query(int p,int x) {
        return query(p,0,n,x);
    }
};

exBSGS

求 x 的值,使得

\[a^x = b (mod\ p) \]

int BSGS(int a,int b,int p,int A = 1, int m = 0) {
    a %= p;
    int k = std::ceil(std::sqrt(p));
    // A * a^(x-m) = b (mod p)
    // y = x - m >= 0
    // y = k * c + d
    // A * a^(k*c) = b * a^(-d)
    // A * a^(k*c+k) = b * a^(k-d)
    std::map<int,int> mp; // k - d
    for(int i=1,nw=mul(b,a,p);i<=k;++i) {
        mp[nw] = i;
        nw = mul(nw,a,p);
    }
    // x = k * c + d + m = k * c + m - (k - d) + k
    int ak = qpow(a,k,p);
    for(int i=0,nw=mul(A,ak,p);i<k;++i) {
        auto it = mp.find(nw);
        if(it != mp.end()) {
            return k * i + m - it->second + k;
        }
        nw = mul(nw,ak,p);
    }
    return -1;
}

int exBSGS(int a,int b,int p) {
    // luogu \sigma p <= 5e6, max 1.11s, avg 333ms
    // a^x = b (mod p)
    a %= p;
    b %= p;
    if(p == 1) return 0;
    int A = 1, m = 0;
    while(true) {
        if(A == b) return m;
        int d = std::__gcd(a,p);
        if(d == 1) break;

        if(b % d != 0) return -1;

        b /= d;
        p /= d;
        A = mul(A, a / d, p);
        ++m;
    }

    return BSGS(a,b,p,A,m);
}

求以下方程的 x 则需要原根+BSGS计算 a 对于原根的对数 z,然后解线性同余方程 ky = z, 答案 x = g^y

\[x^k = a (mod\ p) \]

Linar Equation 线性同余方程

i64 extend_gcd(i64 a,i64 b,i64 &x,i64&y) {
    if(a==0 && b==0) return 0;
    if(b==0) {x=1; y=0; return a;}
    i64 d = extend_gcd(b, a % b, y, x);

    y -= a / b * x;
    return d;
}

std::vector<int> linar_equation(int a,int c,int p) {
    // ax = c (mod p)
    // ax + py = c
    std::vector<int> res;
    i64 x,y;
    int gcd = extend_gcd(a,p,x,y);
    if(c % gcd != 0) return res;
    int k = c / gcd;
    int t = p / gcd;

    x = (x % t + t) % t;
    x = 1ll * x * k % t;

    for(int i = x; i < p; i += t) {
        res.push_back(i);
    }
    return res;
}

杜教筛

O(n^(2/3))

\[ g(1)\cdot S(n)=\sum_{i=1}^n(f * g) (i)-\sum_{d=2}^n g(d)\cdot S(\lfloor \frac{n}{d} \rfloor) \\ (\mu * 1) (n) = e(n) \\ (\phi * 1) (n) = id(n) = n \]

namespace MF { // math function

constexpr int N = 1 << 21;
int mob[N], mob_pre[N];
int n, prm[N];
bool notP[N] = {false};
std::map<i64,i64> mp; // Du_sieve

void get_mob() {
    n = 0;
    mob[0] = mob_pre[0] = 0;
    mob[1] = 1;
    mob_pre[1] = 1;
    for(int i=2;i<N;++i) {
        if(!notP[i]) {
            mob[i] = -1;
            prm[n] = i;
            ++n;
        }

        for(int j = 0; i * prm[j] < N; ++j) {
            register int x = i * prm[j];
            mob[x] = -mob[i];
            notP[x] = true;

            if(i % prm[j] == 0) {
                mob[x] = 0;
                break;
            }
        }
        mob_pre[i] = mob_pre[i - 1] + mob[i];
    }
}

i64 Du_mob(i64 x) {
    if(x < N) return mob_pre[x];
    if(mp[x]) return mp[x];

    i64 res = 1;
    for(i64 l = 2, r; l <= x; l = r) {
        r = x / (x / l) + 1;
        res -= 1ll * Du_mob(x / l) * (r - l);
    }
    mp[x] = res;
    return res;
}

i64 Du_phi(i64 x) {
    i64 res = 0;
    for(i64 l = 1, r; l <= x; l = r) {
        r = x / (x / l) + 1;
        res += 1ll * (x / l) * (x / l) * (Du_mob(r - 1) - Du_mob(l - 1));
    }
    return (res - 1) / 2 + 1;
}

}

字符串哈希

namespace StrHash {

int t = 0;
int pw[2][N];

template<int P,char a,int SIGMA,int id>
struct Hash {
    int n,h;
    Hash() : n(0),h(0) {}
    Hash(char c) : n(1),h(c - a) {}
    Hash(const std::string &s) {
        n = s.length();
        h = 0;
        for(char c : s) {
            h = 1ll * h * SIGMA % P;
            norm(h += c - a);
        }
    }
    void norm(int &x) {
        if(x >= P) x -= P;
        if(x < 0) x += P;
    }
    Hash operator+(const Hash&o) {
        Hash x;
        x.n = n + o.n;
        x.h = 1ll * h * pw[id][o.n] % P;
        norm(x.h += o.h);
        return x;
    }
    Hash operator-(const Hash&o) {
        // this - o;
        // o    = aabbc;
        // this = aabbccdd;
        Hash x;
        x.n = n - o.n;
        x.h = 1ll * o.h * pw[id][x.n] % P;
        norm(x.h = h - x.h);
        return x;
    }
    bool operator!=(const Hash&o) const {
        return n != o.n || h != o.h;
    }
    bool operator==(const Hash&o) const {
        return n == o.n && h == o.h;
    }
    bool operator<(const Hash&o) const {
        if(n != o.n) return n < o.n;
        else return h < o.h;
    }
};

template<int P1,char a1,int SIGMA1,int P2,char a2,int SIGMA2>
struct Hashs {
    using h1 = Hash<P1,a1,SIGMA1,0>;
    using h2 = Hash<P2,a2,SIGMA2,1>;
    h1 x;
    h2 y;
    Hashs() : x(), y() {}
    Hashs(char c) : x(c), y(c) {}
    Hashs(const std::string &s) : x(s), y(s) {}
    bool operator==(const Hashs&o) const {
        return x == o.x && y == o.y;
    }
    bool operator!=(const Hashs&o) const {
        return x != o.x || y != o.y;
    }
    Hashs operator+(const Hashs&o) {
        Hashs r;
        r.x = x + o.x;
        r.y = y + o.y;
        return r;
    }
    Hashs operator-(const Hashs&o) {
        Hashs r;
        r.x = x - o.x;
        r.y = y - o.y;
        return r;
    }
};

constexpr int P1 = 998244353;
constexpr int S1 = 26;
constexpr int P2 = 1000000007;
constexpr int S2 = 29;
using H = Hashs<P1,'a',S1,P2,'a',S2>;
struct DoubleHashArray {
    int n;
    std::vector<H> a;
    DoubleHashArray(std::string s,int siz=0) : n(s.length()), a(n + 1) {
        a.reserve(siz);
        pw[0][0] = pw[1][0] = 1;
        for(;t<n;++t) {
            pw[0][t + 1] = 1ll * pw[0][t] * S1 % P1;
            pw[1][t + 1] = 1ll * pw[1][t] * S2 % P2;
        }
        for(int i=0;i<n;++i) {
            a[i + 1] = a[i] + H(s[i]);
        }
    }
    void extend(char c) {
        ++n;
        if(t<n) {
            pw[0][t + 1] = 1ll * pw[0][t] * S1 % P1;
            pw[1][t + 1] = 1ll * pw[1][t] * S2 % P2;
            ++t;
        }
        a.push_back(a.back() + H(c));
    }
    H query(int l, int r) { // [l, r)
        return a[r] - a[l];
    }
};

}

可持久化并查集

启发式合并

// 启发式合并版
struct Stable_SegT {
    // O(n log^2 n), luogu n=1e5,m=2e5,max162ms
    int n,TOT,tot;
    std::vector<int> t;
    std::vector<int> ls,rs;

    Stable_SegT() {}
    Stable_SegT(int n,int m,const std::vector<int>&a)
           : n(n),TOT(4*n + (std::__lg(n) + 2)*(m + 1) + 6),
             t(TOT),ls(TOT),rs(TOT),tot(0) {
        build(0,n,a);
    }

    int build(int l,int r,const std::vector<int>&a) {
        int p = tot;
        ++tot;
        if(r - l == 1) {
            t[p] = a[l];
            return p;
        }
        int mid = l + r >> 1;
        ls[p] = build(l,mid,a);
        rs[p] = build(mid,r,a);
        return p;
    }

    int query(int p,int l,int r,int v) {
        if(r - l == 1) return t[p];
        int mid = l + r >> 1;
        if(v < mid) return query(ls[p], l, mid, v);
        else return query(rs[p], mid, r, v);
    }

    int modify(int bro,int l,int r,int u,int v) {
        int p = tot;
        ++tot;
        if(r - l == 1) {
            t[p] = v;
            return p;
        }
        ls[p] = ls[bro], rs[p] = rs[bro];
        int mid = l + r >> 1;
        if(u < mid) ls[p] = modify(ls[bro],l,mid,u,v);
        else rs[p] = modify(rs[bro],mid,r,u,v);
        return p;
    }
};

struct Stable_DSU {
    int n;
    Stable_SegT f,sz;
    std::vector<int> rtf,rts;
    Stable_DSU(int n,int m) : n(n), rtf(m + 1), rts(m + 1) {
        std::vector<int> a(n,1);
        sz = Stable_SegT(n,m,a);
        std::iota(a.begin(), a.end(), 0);
        f = Stable_SegT(n,m,a);
    }

    int find(int id,int v) {
        int fa;
        while((fa = f.query(rtf[id],0,n,v)) != v) {
            v = fa;
        }
        return v;
    }
    bool check(int id,int u,int v) {
        return find(id,u) == find(id,v);
    }

    void merge(int now,int lst,int u,int v) {
        u = find(lst,u), v = find(lst,v);
        if(u == v) {
            rtf[now] = rtf[lst];
            rts[now] = rts[lst];
        }
        int su = sz.query(rts[lst],0,n,u), sv = sz.query(rts[lst],0,n,v);
        if(su > sv) std::swap(u,v);
        rtf[now] = f.modify(rtf[lst],0,n,u,v);
        rts[now] = sz.modify(rts[lst],0,n,v,su + sv);
    }
};
// std::vector<int> ver(m + 1);
// std::iota(ver.begin(), ver.end(), 0);

按秩合并

这玩意理论上更优才对,但是被启发式合并爆杀,常数极大,约 3 倍

struct Stable_DSU {
    // O(n log^2 n), luogu n=1e5,m=2e5,max487ms
    int n,TOT,tot;
    std::vector<int> t,d;
    std::vector<int> ls,rs;

    Stable_DSU(int n,int m)
         : n(n),TOT(4*n + (std::__lg(n) + 2)*(m + 1) + 6),
           t(TOT),ls(TOT),rs(TOT),d(TOT),tot(0) {
        build(0,n);
    }

    int build(int l,int r) {
        int p = tot;
        ++tot;
        if(r - l == 1) {
            t[p] = l;
            return p;
        }
        int mid = l + r >> 1;
        ls[p] = build(l,mid);
        rs[p] = build(mid,r);
        return p;
    }

    int query(int p,int l,int r,int v) {
        // WARNING: return the index
        if(r - l == 1) return p;
        int mid = l + r >> 1;
        if(v < mid) return query(ls[p], l, mid, v);
        else return query(rs[p], mid, r, v);
    }

    int find(int p,int v) {
        int fp = query(p,0,n,v);
        return t[fp] == v ? fp : find(p,t[fp]);
    }

    bool check(int p,int u,int v) {
        return t[find(p,u)] == t[find(p,v)];
    }

    int modify(int bro,int l,int r,int u,int v) { // merge u to v
        int p = tot;
        ++tot;
        if(r - l == 1) {
            t[p] = v;
            d[p] = d[bro];
            return p;
        }
        ls[p] = ls[bro], rs[p] = rs[bro];
        int mid = l + r >> 1;
        if(u < mid) ls[p] = modify(ls[bro],l,mid,u,v);
        else rs[p] = modify(rs[bro],mid,r,u,v);
        return p;
    }

    void add(int p,int l,int r,int u) {
        if(r - l == 1) {
            ++d[p];
            return;
        }
        int mid = l + r >> 1;
        if(u < mid) add(ls[p],l,mid,u);
        else add(rs[p],mid,r,u);
    }

    int merge(int bro,int u,int v) {
        int pu = find(bro,u), pv = find(bro,v);
        u = t[pu], v = t[pv];
        if(u == v) return bro;
        if(d[pu] > d[pv]) std::swap(u,v);
        int p = modify(bro,0,n,u,v);
        if(d[pu] == d[pv]) add(p,0,n,v);
        return p;
    }
};

吉如一线段树(维护区间取最值,以及历史最值)

// namespace JRY_SegT
// O(n log n) luogu n,m 5e5 max1.62s
constexpr i64 INF = 0x7fffffffffffffff;
struct Info {
    i64 sum,mx,smx,hmx,cnt;
    Info(i64 sum=0,i64 mx=-INF,i64 smx=-INF,i64 hmx=-INF,i64 cnt=0)
         : sum(sum),mx(mx),smx(smx),hmx(hmx),cnt(cnt) {}
};
Info merge(Info x,Info y) {
    Info r;
    r.sum = x.sum + y.sum;
    r.mx = std::max(x.mx, y.mx);
    r.hmx = std::max(x.hmx, y.hmx);
    if(x.mx == y.mx) r.smx = std::max(x.smx, y.smx), r.cnt = x.cnt + y.cnt;
    else if(x.mx > y.mx) r.smx = std::max(x.smx, y.mx), r.cnt = x.cnt;
    else r.smx = std::max(x.mx, y.smx), r.cnt = y.cnt;
    return r;
}
struct Tag {
    i64 lzm,lzo,hlzm,hlzo;
    Tag() : lzm(0),lzo(0),hlzm(0),hlzo(0) {}
    void clear() {
        lzm = lzo = hlzm = hlzo = 0;
    }
    bool is0() {
        return lzm == 0 && lzo == 0 && hlzm == 0 && hlzo == 0;
    }
};
struct SegT {
    int n;
    std::vector<Info> t;
    std::vector<Tag> z;
    std::vector<int> le,ri;
    SegT(int n,const std::vector<i64>&a) : n(n), t(4*n), z(4*n), le(4*n), ri(4*n) {
        build(1,0,n,a);
    }

    void build(int p,int l,int r,const std::vector<i64>&a) {
        le[p] = l, ri[p] = r;
        if(r - l == 1) {
            t[p] = Info(a[l],a[l],-INF,a[l],1);
            return;
        }
        int mid = l + r >> 1;
        build(p << 1, l, mid, a);
        build(p << 1 | 1, mid, r, a);
        pull(p);
    }

    void pull(int p) {
        t[p] = merge(t[p << 1], t[p << 1 | 1]);
    }

    void update(int p,i64 lzm,i64 lzo,i64 hlzm,i64 hlzo) {
        t[p].sum += lzm * t[p].cnt + (ri[p] - le[p] - t[p].cnt) * lzo;
        t[p].hmx = std::max(t[p].hmx, t[p].mx + hlzm);
        t[p].mx += lzm;
        if(t[p].smx != -INF) t[p].smx += lzo;
        z[p].hlzm = std::max(z[p].hlzm, z[p].lzm + hlzm), z[p].lzm += lzm;
        z[p].hlzo = std::max(z[p].hlzo, z[p].lzo + hlzo), z[p].lzo += lzo;
    }

    void pushdown(int p) {
        if(z[p].is0()) return;
        int mx = std::max(t[p << 1].mx, t[p << 1 | 1].mx);
        for(auto ps : {p << 1, p << 1 | 1}) {
            if(t[ps].mx == mx) update(ps, z[p].lzm, z[p].lzo, z[p].hlzm, z[p].hlzo);
            else update(ps, z[p].lzo, z[p].lzo, z[p].hlzo, z[p].hlzo);
        }
        z[p].clear();
    }

    void range_add(int p,int L,int R,i64 v) {
        if(ri[p] <= L || R <= le[p]) return;
        if(L <= le[p] && ri[p] <= R) {
            update(p,v,v,v,v);
            return;
        }
        pushdown(p);
        range_add(p << 1, L, R, v);
        range_add(p << 1 | 1, L, R, v);
        pull(p);
    }

    void range_min(int p,int L,int R,i64 v) {
        if(ri[p] <= L || R <= le[p] || t[p].mx <= v) return;
        if(L <= le[p] && ri[p] <= R && t[p].smx < v) {
            update(p, v - t[p].mx, 0, v - t[p].mx, 0);
            return;
        }
        pushdown(p);
        range_min(p << 1, L, R, v);
        range_min(p << 1 | 1, L, R, v);
        pull(p);
    }

    i64 query_sum(int p,int L,int R) {
        if(ri[p] <= L || R <= le[p]) return 0ll;
        if(L <= le[p] && ri[p] <= R) return t[p].sum;
        pushdown(p);
        return query_sum(p << 1, L, R) + query_sum(p << 1 | 1, L, R);
    }

    i64 query_mx(int p,int L,int R) {
        if(ri[p] <= L || R <= le[p]) return -INF;
        if(L <= le[p] && ri[p] <= R) return t[p].mx;
        pushdown(p);
        return std::max(query_mx(p << 1, L, R), query_mx(p << 1 | 1, L, R));
    }

    i64 query_hmx(int p,int L,int R) {
        if(ri[p] <= L || R <= le[p]) return -INF;
        if(L <= le[p] && ri[p] <= R) return t[p].hmx;
        pushdown(p);
        return std::max(query_hmx(p << 1, L, R), query_hmx(p << 1 | 1, L, R));
    }
};

离散化二维前缀和

使用方法:

  1. 将插入的点以 d2p.insert(x,y,val); 插入
  2. 将想要询问的点以 d2p.insert(x,y,0); 提前插入
  3. d2p.work()
  4. d2p.query(x0,y0 ~ x3,y3)
struct Discrete_2D_Prefix {
    // manhattan distance:
    // x' = x - y, y' = x + y
    // equal to rotate the axis for +45 degree
    using pii = std::pair<int,int>;
    std::map<pii,int> mp;
    std::vector<int> vy;
    bool worked;
    Discrete_2D_Prefix() : worked(false) {
        vy.reserve(2e6);
    }
    void insert(int x,int y,int val) {
        mp[pii(x,y)] += val;
        vy.push_back(y);
    }

    void work() {
        std::sort(vy.begin(), vy.end());
        vy.erase(std::unique(vy.begin(), vy.end()), vy.end());

        Fenwick<int> t(vy.size());

        auto get = [&](int y) {
            return std::lower_bound(vy.begin(), vy.end(), y) - vy.begin();
        };

        for(auto it = mp.begin(), jt = mp.begin(); it != mp.end(); it = jt) {
            jt = std::next(it);
            while(jt != mp.end() && it->first.first == jt->first.first) {
                ++jt;
            }

            for(auto kt = it; kt != jt; ++kt) {
                if(kt->second) {
                    t.isr(get(kt->first.second),1);
                }
            }

            for(auto kt = it; kt != jt; ++kt) {
                kt->second = t.qry(get(kt->first.second));
            }
        }

        worked = true;
    }

    int query(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3) {
        assert(worked); // Hav'you get it worked?
        return mp[pii(x0,y0)] - mp[pii(x1,y1)] - mp[pii(x2,y2)] + mp[pii(x3,y3)];
    }
};

Moblus Inversion

\[\begin{align} \sum _{i=1} ^{n} \frac{1}{gcd(i,n)} &= \sum _{d|n} \frac{\phi(\frac{n}{d})}{d} \\ \sum _{i=1} ^{n-1} i \ \text{op} \ \gcd(i, n) &= \frac{1}{2} (\sum _{i=1} ^{n-1} i \ \text{op} \ \gcd(i, n) + \sum _{i=1} ^{n-1} (n - i) \ \text{op} \ \gcd(i, n)) \end{align} \]

CDQ divide and conquer

std::function<void(int,int)> CDQ = [&](int l,int r) {
    int mid = l + r >> 1;
    if(r - l == 1) {
        int i = l;
        f[i + 1] = add(f[i + 1], mul(R[i], add(f[i], c[i])));
        return;
    }
    CDQ(l, mid);

    poly g(f.begin() + l, f.begin() + mid);
    poly h(w.begin(), w.begin() + r - l);
    g = Poly::mul(g, h);
    int m = (int)g.size();
    for(int I=mid-l,i=l+I;I<m&&i<r;++I,++i) {
        f[i + 1] = add(f[i + 1], MOD - mul(mul(R[i], Q[i]), g[I]));
    }

    CDQ(mid, r);
};
CDQ(0,n+1);

手写 bitset (from abc241)

struct bitset{
    unsigned long long arr[3130]={};
    unsigned long long AF=-1ull;
    
    void flip(int l,int r){
        arr[l/64]^=(1ull<<(l%64))-1;
        if (r%64==63) arr[r/64]^=AF;
        else arr[r/64]^=(1ull<<(r%64+1))-1;
        l/=64,r/=64;
        if (l==r) return;
        arr[l]^=AF;
        
        for (int x=l+1;x<r;x++) arr[x]^=AF;
    }
    
    int get(int i){
        if (arr[i/64]&(1ull<<(i%64))) return 1;
        else return 0;
    }
    
    int get1(int i){
        //search [i%64,64) on i/64 first
        unsigned long long mask=AF^((1ull<<(i%64))-1);
        
        i=i/64;
        unsigned long long temp=arr[i]&mask;
        if (temp) return i*64+__builtin_ctzll(temp);
        i++;
        while (true){
            if (arr[i]==0) i++;
            else return i*64+__builtin_ctzll(arr[i]);
        }
    }
    
    int get0(int i){
        //search [i%64,64) on i/64 first
        unsigned long long mask=AF^((1ull<<(i%64))-1);
        
        i=i/64;
        unsigned long long temp=(arr[i]^AF)&mask;
        if (temp) return i*64+__builtin_ctzll(temp);
        i++;
        while (true){
            if (arr[i]==AF) i++;
            else return i*64+__builtin_ctzll(arr[i]^AF);
        }
    }
    
    int gethigh(){
        int i=3129;
        while (true){
            if (arr[i]==0) i--;
            else return i*64+63-__builtin_clzll(arr[i]);
        }
    }
} BS;

Stiring Number

\[\begin{align} S_1(n, m) &= S_1(n - 1, m - 1) + (n - 1) \cdot S_1(n - 1, m) \\ S_2(n, m) &= S_2(n - 1, m - 1) + m \cdot S_2(n - 1, m) \end{align} \]

signed S1 Pascal Triangle

1
0 1
0 -1 1
0 2 -3 1
0 -6 11 -6 1
0 24 -50 35 -10 1
0 -120 274 -225 85 -15 1
0 720 -1764 1624 -735 175 -21 1

S2 Pascal Triangle

1
0 1
0 1 1
0 1 3 1
0 1 7 6 1
0 1 15 25 10 1
0 1 31 90 65 15 1
0 1 63 301 350 140 21 1
0 1 127 966 1701 1050 266 28 1
0 1 255 3025 7770 6951 2646 462 36 1

Stiring Expansion

\[m ^ n = \sum _{k=0} ^ \infin P(m, k) \cdot S_2(n, k) \]

积性函数筛转移

其实就是个根据最小质因子的 DP 转移

  1. 计算 \(f(p)\)
  2. 计算 \(f(p^k)\)\(f(p^{k+1})\) 的转移
  3. 计算 \(f(i)\)\(f(i \cdot p)\) 的转移 (\(p|i\)), 即 \(f(a \cdot p^w)\)\(f(a \cdot p^{w+1})\) (\(a \bot p\)), 常用差分, 对除的技巧

以上转移均要求能快速计算

SPFA

auto spfa = [&]() {
    std::vector<int> inQ(n + 1, false), cnt(n + 1);
    std::deque<int> q;

    q.push_back(0);
    inQ[0] = true;
    cnt[0] = 1;
    while(!q.empty()) {
        auto v = q.front();
        q.pop_front();
        inQ[v] = false;

        for(auto e : g[v]) {
            int to = e[0], cost = e[1];
            if(dis[v] + cost < dis[to]) {
                dis[to] = dis[v] + cost;
                if(!inQ[to]) { // SLF
                    if(!q.empty() && dis[to] < dis[q.front()]) q.push_front(to);
                    else q.push_back(to);
                    inQ[to] = true;
                    ++cnt[to];
                    if(cnt[to] >= n) {
                        return false;
                    }
                }
            }
        }
    }
    return true;
};

树背包

void dfs(int v,int f) {
    sz[v] = 1;
    dp[v][0][0] = 1;
    dp[v][0][1] = 0;
    // i64 tot = 1;
    for(int to : g[v]) if(to!=f) {
        dfs(to,v);
        for(int i=even(sz[v]);i>=0;i-=2) {
            for(int j=0;j<=sz[to];j+=2) {
                if(j) {
                    norm(
                        dp[v][i+j][0] += (dp[v][i][0] * (dp[to][j][0] + dp[to][j][1])) % MOD
                    );
                    norm (
                        dp[v][i+j][1] += (dp[v][i][1] * (dp[to][j][0] + dp[to][j][1])) % MOD
                    );
                }
                norm (
                    dp[v][i+j+2][1] += (dp[v][i][0] * dp[to][j][0]) % MOD
                );
            }
        }
        sz[v] += sz[to];
    }
}

奇怪的优化

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")

珂朵莉树

#include <bits/stdc++.h>
using i64 = long long;
using pii = std::pair<int,int>;
 
template<typename T>
struct Fenwick {
    int n;
    std::vector<T> t;
    Fenwick(int n) : n(n+6),t(n+6) {}
    void isr(int p,T v) {
        for(++p;p<n;p+=p&-p) t[p] += v;
    }
    T qry(int p) {
        T res = 0;
        for(++p;p;p-=p&-p) res += t[p];
        return res;
    }
};
 
int main(int argc, char const *argv[])
{
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr); std::cout.tie(nullptr);
 
    int n,q;
    std::cin >> n >> q;
    std::vector<i64> tag(n);
    std::map<int,int> col;
    col[0] = 0;
    col[n] = -1;
 
    auto split = [&](int p) {
        auto it = std::prev(col.upper_bound(p));
        col[p] = it->second;
    };
 
    Fenwick<i64> t(n);
 
    auto RangeAdd = [&](int l,int r,i64 v) {
        t.isr(l,v);
        t.isr(r,-v);
    };
 
    for(;q;--q) {
        std::string op;
        std::cin >> op;
        if(op[0] == 'C') {
            int l,r,c;
            std::cin >> l >> r >> c;
            --l;
            --c;
 
            split(l);
            split(r);
 
            auto it = col.find(l);
            while(it->first != r) {
                RangeAdd(it->first, std::next(it)->first, tag[it->second] - tag[c]);
                it = col.erase(it);
            }
            col[l] = c;
        }else if(op[0] == 'A') {
            int c;
            i64 v;
            std::cin >> c >> v;
            --c;
 
            tag[c] += v;
        }else {
            int p;
            std::cin >> p;
            --p;
 
            auto it = std::prev(col.upper_bound(p));
            std::cout << t.qry(p) + tag[it->second] << '\n';
        }
    }
 
    return 0;
}
posted @ 2021-06-21 22:51  LacLic  阅读(527)  评论(2编辑  收藏  举报