算法板子
前言
这个板子跨时间较长,越后面的代码/封装的越好的代码越靠近本人最新的代码习惯,一些带 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\)
手写哈希表(比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
}
牛顿二项式定理与生成函数
k阶前缀/差分(NTT快速幂)
前缀和:\(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 的值,使得
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
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))
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));
}
};
离散化二维前缀和
使用方法:
- 将插入的点以
d2p.insert(x,y,val);
插入 - 将想要询问的点以
d2p.insert(x,y,0);
提前插入 d2p.work()
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
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
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
积性函数筛转移
其实就是个根据最小质因子的 DP 转移
- 计算 \(f(p)\)
- 计算 \(f(p^k)\) 到 \(f(p^{k+1})\) 的转移
- 计算 \(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;
}