模板整合计划 写了一点qwq慢慢补
板子整合计划
求查错\(qwq\)
不开\(long~long\)爆的连裤衩都没了
inline int read(){//快读 cctype iostream
int x = 0; bool op = 0;char c = getchar();
while(!isdigit(c))op |= (c == '-'), c = getchar();
while(isdigit(c))x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return op ? -x : x;
}
inline int read(){
int x = 0; bool op = 0;char c = getchar();
while(c > '9' || c < '0')op |= (c == '-'), c = getchar();
while(c>='0'&&c<='9')x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return op ? -x : x;
}
找树的直径 两遍\(dfs\)写法
dep[N] = {-1};
void dfs(int x,int fa){
dep[x] = dep[fa] + 1;
if(dep[x] > dep[maxx]) maxx = x;//获取最深节点
for(int i = head[x];i;i = e[i].next){
int v = e[i].to; if(v == fa) continue;
dfs(v,x);
}
}
main(){
dfs(1,0); dfs(maxx,0);
cout<<dep[maxx];
}
换根dp
void dp(int x){
vis[x] = 1;
for(int i = head[x];i;i = e[i].next){
int v = e[i].to;
if(vis[v]) continue; dp(y);
ans = max(ans,dep[x]+dep[y]+e[i].w);
dep[x] = max(dep[x],d[y] + e[i].w);
}
}
kruskal最小生成树(最大把小于换大于)
struct edge_tu{int from,to,w;}e1[N<<1];
struct edge_tree{int from,to,w;}e2[N<<1];
void add(int u,int v,int w){
e2[++tot] = (edge_tree){head[u],v,w}; head[u] = tot;
}
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
bool cmp(edge_tu a,edge_tu b){
return a.w < b.w;
}
void kruskal(){
sort(e1 + 1,e1 + m + 1,cmp);
for(int i = 1;i <= m;++i){
int x = find(e1[i].x),y = find(e1[i]);
if(x == y) continue; fa[x] = y;
add(e1[i].u.e1[i].v,e1[i].w);
add(e1[i].v, e1[i].u, e1[i].w);
if (++num == n) return;
}
}
并查集
int find(int x){
if(x == fa[x]) return x;//找到辈最大的
return fa[x] = find(fa[x]);//递归写法,路径压缩,不停加辈,爷爷变成爸爸,保证以后询问的话只向上跳一次
}
inline int find(int x){
while(fa[fa[x]] != fa[x]) fa[x] = fa[fa[x]];
return fa[x];//循环写法,调动栈空间少且容易理解
//实际貌似又大了又慢了
}
int main(){
cin >> n >> m; int q,x,y;
for(int i = 1;i <= n;++i) fa[i] = i;//初始爸爸是自己
for(int i = 1;i <= m;++i){
q = read(); x = read(); y = read();
if(q & 1) fa[find(x)] = find(y);//统一爸爸
else (find(x) == find(y)) ? puts("Y") : puts("N");//同一集合爸爸一样
}
}
快速幂龟速乘
int qpow(int a,int b){
int ans = 1;
for(;b;b >>= 1){
if(b & 1) ans = (long long)ans * a % mod;
a = a * a % mod;
}
return ans;
}
ll mul(ll a,ll b){
ll ans = 0;
for(;b;b >>= 1){
if(b & 1) ans = (ans + a) % mod;
a = (a + a) % mod;
}
return ans;
}
\(a*b~mod~p=a*b-\lfloor a*b/p\rfloor*p\)
线段树 单点修改 区间乘 区间加 区间和
struct node{int v,add,mul;}tree[N<<2];
void pushdown(int root,int l,int r){//维护lazy
int mid = (l + r)>>1;//儿子的值*爸爸的mul+儿子的区间长度*爸爸的add 先求和,再lazy
tree[root<<1].v = (tree[root<<1].v * tree[root].mul + tree[root].add * (mid - l + 1))%p;
tree[root<<1|1].v = (tree[root<<1|1].v*tree[root].mul+tree[root].add*(r - mid))%p;
tree[root<<1].mul = (tree[root].mul * tree[root<<1].mul)%p;
tree[root<<1|1].mul = (tree[root].mul * tree[root<<1|1].mul)%p;
tree[root<<1].add = (tree[root].mul * tree[root<<1].add + tree[root].add)%p;
tree[root<<1|1].add = (tree[root].mul * tree[root<<1|1].add + tree[root].add)%p;
tree[root].mul = 1; tree[root].add = 0; return;
}
void change(int root,int l,int r,int x,int data){
if(l == r) {
tree[root].v = data;
tree[root].add = 0; tree[root].mul = 1; return;
}
pushdown(root,l,r);
int mid = (l + r) >>1;
if(x <= mid) change(root<<1,l,mid,x,data);
else change(root<<1|1,mid+1,r,x,data);
tree[root].v = (tree[root<<1].v + tree[root<<1|1].v) % p;
}
void build(int root,int l,int r){
tree[root].mul = 1;
tree[root].add = 0;
if(l == r) tree[root].v = a[l];//
else{
int mid = (l + r)>>1;
build(root<<1,l,mid); build(root<<1|1,mid +1,r);
tree[root].v = tree[root<<1].v + tree[root<<1|1].v;
}
tree[root].v %= p;
return;
}
void tmul(int root,int stdl,int stdr,int l,int r,int k){
if(r < stdl || l > stdr) return;
if(l <= stdl && r >= stdr){
tree[root].v = (tree[root].v * k) % p;
tree[root].mul = (tree[root].mul * k) % p;
tree[root].add = (tree[root].add * k) % p;
return;
}
pushdown(root,stdl,stdr);
int mid = stdl + stdr >>1;
tmul(root<<1,stdl,mid,l,r,k); tmul(root<<1|1,mid+1,stdr,l,r,k);
tree[root].v = (tree[root<<1].v + tree[root<<1|1].v) % p;
return;
}
void tadd(int root,int stdl,int stdr,int l,int r,int k){
if(r < stdl || l > stdr) return;
if(l <= stdl && r >= stdr){
tree[root].add = (tree[root].add + k) % p;
tree[root].v = (tree[root].v + k * (stdr - stdl + 1)) % p;
return;
}
pushdown(root,stdl,stdr);
int mid = stdl + stdr >> 1;
tadd(root<<1,stdl,mid,l,r,k); tadd(root<<1|1,mid+1,stdr,l,r,k);
tree[root].v = (tree[root<<1|1].v + tree[root<<1].v) % p;
return;
}
int query(int root,int stdl,int stdr,int l,int r){
if(r < stdl || l > stdr) return 0;
if(l <= stdl && r >= stdr) return tree[root].v;
pushdown(root,stdl,stdr);
int mid = (stdl + stdr) >> 1;
return (query(root<<1,stdl,mid,l,r) + query(root<<1|1,mid+1,stdr,l,r)) % p;
}
树状数组 单点+ 区间查询 逆序对 区间修改
inline int lowbit(int x){return x & (-x);}
void build(){
for(int i = 1;i <= n;++i) cin >> a[i],sum[i] = sum[i-1] + a[i];
for(int i = 1;i <= n;++i) tre[i] = sum[i] - sum[i - lowbit(i)];
//每个树状数组i维护lowbit(i)的长度
}//O(n)建树法,sum是前缀和
inline void add(int a,int b){
for(;a <= n;a += lowbit(a)) c[a] += b;
}//单点修改
inline int ask(int i){
int ans = 0;
for(;i;i -= lowbit(i)) ans += c[i];
return ans;
}
//逆序对
for(int i = n;i;--i){
ans += ask(a[i]-1);
add(a[i],1);
}
//区间修改
inline int query(int x){
int ans = 0;
for(int i = x;i;i -= lowbit(i))
ans += (x + 1) * c1[i] - c2[i];
return ans;
}
inline void add(int x,int a){
for(int i = x;i <= n;i += lowbit(i)) c1[i] += a,c2[i] += x * a;
}
main:
add(i,a[i]-a[i-1]);
\(RMQ - ST\)
\(f[i][j]\)表示从\(a[i]\)到\(a[i+2^j-1]\)的最大值/最小值
把\(f[i][j]\)分成\(f[i][j-1]\)和\(f[i+2^{j-1}][j-1]\)
\(f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1])\)
边界\(f[i][0]=a[i]\)
log[0] = -1;
for(int i = 1;i <= n;++i) log[i] = log[i>>1] + 1;
for(int j = 1;j <= 20;++j)
for(int i = 1;i+(1<<j)-1 <= n;++i)
f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
//求区间[x,y]最大值
int k = log[y-x+1];
ans = max(f[x][k],f[y-(1<<k)+1][k])
离散化
memcpy(a,c,sizeof(c));
sort(a + 1,a + n + 1);
int siz = unique(a + 1,a + n + 1)- a - 1;
for(int i = 1;i <= n;++i) b[i] = lower_bound(a + 1,a + siz + 1,a[i])-a+1;
归并排序求逆序对
inline void q_merge(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
q_merge(l, mid);
q_merge(mid + 1, r);
int i = l, j = mid + 1, k = l;
while(i <= mid || j <= r) {
if (j > r || i <= mid && a[i] <= a[j]) b[k ++] = a[i ++];
else {
b[k ++] = a[j ++];
ans += mid - i + 1;
}
}
for (int i = l; i <= r; i ++) a[i] = b[i];
return;
}
tarjan找割边 \(dfn[x]<low[y]\)
//tarjan找割边/桥
void tarjan(int x,int in_edge){
dfn[x] = low[x] = ++num;
for(int i = head[x];i;i = e[i].next){
int v = e[i].to;
if(!dfn[v]){
tarjan(v,i);
low[x] = min(low[x],low[y]);
if(low[y] > dfn[x]) bridge[i] = bridge[i ^ 1] = 1;
}
else if(i != (in_edge ^ 1))
low[x] = min(low[x],dfn[v]);
}
}
割点判定\(dfn[x]\le low[y]\)
void tarjan(int x){
dfn[x] = low[x] = ++num; int flag = 0;
for(int i = head[x];i;i = e[i].next){
int v = e[i].to;
if(!dfn[y]){
tarjan(y);
low[x] = min(low[x],low[y]);
if(low[y] >= dfn[x]){
++flag;
if(x != root || flag > 1) cut[x] = true;
}
}
}
}
树链剖分
void dfs1(int x,int f){
dep[x] = dep[f] + 1; fa[x] = f; siz[x] = 1;
for(int i = head[x];i;i = e[i].next){
if(v == f) continue;
dfs1(v,x);
siz[x] += siz[v];
if(siz[son[x]] < siz[v]) son[x] = v;
}
}
void dfs2(int x,int tp){
top[x] = tp;
dfn[x] = ++cnt; rk[cnt] = x;
if(son[x]) dfs2(son[x],tp);
for(int i = head[x];i;i = e[i].next){
int v = e[i].to;
if(v == fa[x] || v == son[x]) continue;
dfs2(v)
}
}
//树剖求lca
int lca(int x,int y){
int fx = top[x],fy = top[y];
while(fx != fy){
if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy);
x = fa[top[x]]; fx = top[x];
}
return dep[x] > dep[y] ? y : x;
}
\(dijkstra\)板子 求到\(s\)的单源最短路径
priority_queue<pair<int,int> >q;
void dijkstra(){
memset(d,0x3f,sizeof(d));
d[s] = 0;
q.push(make_pair(0,s));
while(q.size()){
int x = q.top().second; q.pop();
if(v[x]) continue;
v[x] = 1;
for(int i = head[x];i;i = e[i].next){
int y = e[i].to,z = e[i].w;
if(d[y] > d[x] + z){
d[y] = d[x] + z;
q.push(make_pair(-d[y],y));
}
}
}
}
\(spfa\)
void spfa(){
memset(d,0x3f,sizeof(d));
d[s] = 0; queue<int> q;
q.push(s); vis[s] = 1;
while(q.size()){
int x = q.front(); q.pop();
vis[x] = 0;
for(int i = head[x];i;i = e[i].next){
int v = e[i].to,z = e[i].w;
if(d[v] > d[x] + z){
d[v] = d[x] + z;
if(!vis[v]) q.push(v),vis[v] = 1;
}
}
}
}
\(floyed\)传递闭包/距离 \(bitset\)优化
bitset<N>f[N];
for(int k = 1;k <= n;++k)
for(int i = 1;i <= n;++i)
for(int j = 1;j <= n;++j)
f[i][j] |= f[i][k] & f[k][j];
//f[i][j] = min(f[i][j],min(f[i][k],f[k][j]));
倍增$LCA $ \(dfs\)中顺便处理\(LCA\)
void dfs1(int u,int f){
dep[u] = dep[f] + 1; fa[u][0] = f;
for(int j = 1;j <= 20;++j) fa[u][j] = fa[u][fa[u][j-1]];
for(int i = head[u];i;i = e[i].next){
int v = e[i].to; if(v == f) continue;
dfs1(v,u);
}
}
inline int lca(int x,int y){
if(dep[x] < dep[y]) swap(x,y);
for(int i = 20;i >= 0;--i){
if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if(x == y) return x;
}
for(int i = 20;i >= 0;--i){
if(fa[x][i] != fa[y][i]){
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}
树上差分
int dfs2(int x,int f){
for(int i = head[x];i;i = e[i].next){
int v = e[i].to;
if(v == f) continue;
dfs2(v,x);
cha[x] += cha[v];
}
}
for(int i = 1;i <= k;++i){
u = read(); v = read();
++cha[u]; ++cha[v]; cha[lca(u,v)] -= 2;//边差分
}
for(int i = 1;i <= k;++i){
u = read(); v = read(); int z = lca(u,v);
++cha[u]; ++cha[v]; --cha[z]; --cha[fa[z]];//点差分
}
已知中后序遍历,求先序遍历
inline int find(char ch){
for(int i = 0;i < len;++i)
if(s1[i] == ch) return i;
}
void dfs(int l1,int r1,int l2,int r2){
int m = find(s2[r2]);
cout << s2[r2];
if(m > l1) dfs(l1,m - 1,l2,r2 - r1 + m - 1);
if(m < r1) dfs(m + 1,r1,l2 + m -l1.r2 -1);
}
板子整合计划2
杂项 $ :C_nm=C_{n-1}+C_{n-m}^m$ \(vector的end\)是越界的
整体集合上的二分
while(l <= r){
int mid = l + r >>1;
if(...) r = mid - 1;
else r = mid + 1;
}
\(e-DCC\)
void dfs(int x){
c[x] = dcc;
for(int i = head[x];i;i = e[i].next){
int v = e[i].to;
if(c[v] || bridge[i]) continue;
dfs(v);
}
}
main(){
for(int i = 1;i <= n;++i)
if(!c[i]){
++dcc;
dfs(i);
}
//dcc:有几个 c[i]:i belong to which gcc
//还得用tarjan找桥
}
链表
struct node{
int val,prev,next;
}node[size];
int head,tail,tot;
int init(){
tot = 2;
head = 1;tail = 2;
node[head].next = tail;
node[tail].prev = head;
}
int insert(int p,int val){
q = ++tot;
node[q].val = val;
node[node[p].next].prev = q;
node[p].next = q;
node[q].next = p;
}
void remove(int p){
node[node[p].prev].next = node[p].next;
node[node[p].next] = node[p].prev;
}
void clear(){
memset(node,0,sizeof(node));
head = tail = tot = 0;
}
\(O(nloglogn)\)埃氏筛
void primes(int n){
memser(vis,0,sizeof(vis));
for(int i = 2;i <= n;++i){
if(vis[v]) continue;
printf("%d",i); //i是质数
for(int j = i;j <= n/i;++j) vis[i * j] = 1;
}
}
\(O(n)\)线性筛
void primes(int n){
memset(vis,0,sizeof(vis));
num = 0;//素数数量
for(int i = 2;i <= n;++i){
if(!vis[i]) vis[i] = prime[++num] = i;
for(int j = 1;j <= num;++j){
//i有比prime[j]更小的质因子,或者超过n的范围,break
if(prime[j] > vis[j] || prime[j] > n/i) break;
vis[i * prime[j]] = prime[j];
}
}
}
\(exgcd\)
\(ax+by=c\) \(gcd(a,b)|c\)有解,通解为\(x=\frac cdx_0+\frac bdt,y=\frac cdy_0-\frac adt,t\in z,d=gcd(a,b)\)
int exgcd(int a,int b,int &x,int &y){
if(b == 0){x = 1,y = 0,return a;}
int d = exgcd(b,a % b,x,y);
int z = x;x = y;y = z - y * (a/b);
return d;
}
欧拉筛phi
int v[N],phi[N],prime[N];
void euler(int n){
memset(vis,0,sizeof(vis));
num = 0;//质数
for(int i = 2;i <= n;++i){
if(!vis[i]){
vis[i] = prime[++num]= i;
phi[i] = i-1;
}
for(int j = 1;j <= num;++j){
if(prime[j] > vis[i] || prime[j] > n/i) break;
vis[i * prime[j]] = prime[j];
phi[i * prime[j]] = phi[j];
vis[i * prime[j]] =
phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
}
}
}
phi
int phi(int n){
int ans = n; int az = sqrt(n);
for(int i = 2;i <= az;++i)
if(n % i == 0)[
ans = ans/ i * (i-1);
while(n % i == 0) n /= i;
]
if(n > 1) ans = ans / n * (n-1);
}
线性求逆元
for (int i = 2;i <= n;++i){
inv[i] = -mod/i * inv[mod%i] % mod;
inv[i] = (inv[i] + mod) % mod;
}
\(O(n)\)查\(k\)大
int find(int l,int r) {
if(l == r && l == k) return a[k];
if(l < r) {
int i = l,j = r;
int tmp = a[l];
while(i < j) {
while(i < j &&a[j] >= tmp) --j;
if(i < j) swap(a[i],a[j]);
while(i < j && a[i] <= tmp) ++i;
if(i < j) swap(a[i],a[j]);
}
a[i] = tmp;
if(i == k) return a[k];
if(i > k) return find(l,i - 1);
else return find(i + 1,r);
}
}