首页 > 其他分享 >线段树

线段树

时间:2023-04-27 18:33:34浏览次数:34  
标签:cnt return int 线段 mid ++ void

 

1.基础算法

1.1快读快写

template <typename T> inline void read(T& t) {

 int f = 0, c = getchar(); t = 0;

 while (!isdigit(c)) f |= c == '-', c = getchar();

 while (isdigit(c)) t = t * 10 + c - 48, c = getchar();

 if (f) t = -t;

}



template <typename T> void print(T x) {

 if (x < 0) x = -x, putchar('-');

 if (x > 9) print(x / 10);

 putchar(x % 10 + 48);

}

 

1.2二分、三分

区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用

int bsearch_1(int l, int r)

{

 while (l < r)

{

    int mid = l + r >> 1;

    if (check(mid)) r = mid;

    else l = mid + 1;

}

 return l;

}

//区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用

int bsearch_2(int l, int r)

{

 while (l < r)

{

    int mid = l + r + 1 >> 1;

    if (check(mid)) l = mid;

    else r = mid - 1;

}

 return l;

}

double bsearch_3(double l, double r)

{

 const double eps = 1e-6;  eps //表示精度,取决于题目对精度的要求

 while (r - l > eps)

{

    double mid = (l + r) / 2;

    if (check(mid)) r = mid;

    else l = mid;

}

 return l;

}



//三分法

double l, r;

const double eps

while(r - l > eps) {

 double mid1 = l + (r - l) / 3.0;

 double mid2 = r - (r - l) / 3.0;

 double fx = check(mid1), fy = check(mid2);

 if(fx > fy) l = mid1;

 else r = mid2;

}

 

1.3前缀和

 

//一维前缀和:快速查询区间和

s[i] = s[i-1]+a[i]

a[l] + ... + a[r] = s[r] - s[l - 1]



//二维前缀和:子矩阵和

//s[i, j] = 第i行j列格子左上部分所有元素的和

//以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:

s[x2, y2] - s[x1 - 1, y2] - s[x2, y1 - 1] + s[x1 - 1, y1 - 1]



//s[i]处理:

for(int i = 1; i <= n; i++) {

 for(int j = 1; j <= m; j++) {

    s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];

}

}



//一维差分

//区间[l, r]中的每个数加上c:b[l] += c, b[r + 1] -= c

//前缀和的逆运算就是差分

void insert(int l, int r, int x) {

 b[l] += x;

 b[r + 1] -= x;

}

//二维差分

//以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:

s[x1, y1] += c, s[x2 + 1, y1] -= c, s[x1, y2 + 1] -= c, s[x2 + 1, y2 + 1] += c



void insert(int x1, int y1, int x2, int y2, int c) {

 b[x1][y1] += c;

 b[x2 + 1][y1] -= c;

 b[x1][y2 + 1] -= c;

 b[x2 + 1][y2 + 1] += c;

}

//差分处理原数组

for(int i = 1; i <= n; i++) {

 for(int j = 1; j <= m; j++) {

    insert(i, j, i, j, a[i][j]);

}

}

//还原

for(int i = 1; i <= n; i++) {

 for(int j = 1; j <= m; j++) {

    b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];

}

}

 

1.4离散化

/*

*第一种方式:stl函数(绝对的大小关系)

*n 原数组大小

*num 原数组中的元素 lsh 离散化的数组 cnt 离散化后的数组大小

*/

int lsh[MAXN], cnt, num[MAXN], n;

for(int i = 1; i <= n; i++) {

 scanf("%d", &num[i]);

 lsh[i] = num[i];

}

sort(lsh + 1, lsh + n + 1);



cnt = unique(lsh + 1, lsh + n + 1) - lsh - 1;

for(int i = 1; i <= n; i++)

num[i] = lower_bound(lsh + 1, lsh + cnt + 1, num[i]) - lsh;



/*

*第二种方式:记录下标,排序后放回原数组

*/

#include<algorithm>

struct Node {

 int data, id;

 bool operator < (const Node &a) const {

   return data < a.data; //如果有位置要求 记得加

}

};

Node num[MAXN];

int rank[MAXN], n;

for(int i = 1; i <= n; i++) {

 scanf("%d", &num[i].data);

 num[i].id = i;

}

sort(num + 1, num + n + 1);

for(int i = 1; i <= n; i++)

 rank[num[i].id] = i;

 

1.5高精度

 

/*

*加法

*/

\#include <bits/stdc++.h>

using namespace std;

vector<int> add(vector<int> &a, vector<int> &b) {

vector<int> c;

int t = 0;

for(int i = 0; i < a.size() || i < b.size(); i++) {

if(i < a.size()) t += a[i];

if(i < b.size()) t += b[i];

c.push_back(t % 10);

t /= 10;

}

if(t) c.push_back(1);

return c;

}

int main() {

vector<int> A, B;

string a, b;

cin >> a >> b;

for(int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');

for(int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');

auto ok = add(A, B);

for(int i = ok.size() - 1; i >= 0; i--) {

cout << ok[i];

}

return 0;

}

/*

*减法C = A - B, 满足A >= B, A >= 0, B >= 0

*/

bool cmp(vector<int> &a, vector<int> &b) {

if(a.size() != b.size()) return a.size() > b.size();



for(int i = a.size() - 1; i >= 0; i--) {

if(a[i] != b[i]) return a[i] > b[i];



}

return true;

}

vector<int> sub(vector<int> &A, vector<int> &B) {

vector<int> C;

for (int i = 0, t = 0; i < A.size(); i ++ ) {

t = A[i] - t;

if (i < B.size()) t -= B[i];

C.push_back((t + 10) % 10);

if (t < 0) t = 1;

else t = 0;

}



while (C.size() > 1 && C.back() == 0) C.pop_back();

return C;

}

signed main() {

string a, b;

cin >> a >> b;

vector<int> A, B;

for(int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');

for(int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');

if(cmp(A, B)) {

auto c = sub(A, B);

for(int i = c.size() - 1; i >= 0; i--) {

cout << c[i];

}

} else {

auto c = sub(B, A);

cout << "-";

for(int i = c.size() - 1; i >= 0; i--) {

cout << c[i];

}

}

return 0;

}



/*

*除法 A / b = C ... r, A >= 0, b > 0

*/

vector<int> div(vector<int> &A, int b, int &r) {

vector<int> C;

r = 0;

for (int i = A.size() - 1; i >= 0; i -- ) {

r = r * 10 + A[i];

C.push_back(r / b);

r %= b;

}

reverse(C.begin(), C.end());

while (C.size() > 1 && C.back() == 0) C.pop_back();

return C;

}

signed main() {

string a;

int b;

int r = 0;

cin >> a >> b;

vector<int> A;

for(int i = a.size() - 1; i >= 0; i--) {

A.push_back(a[i] - '0');

}

auto c = div(A, b, r);

for(int i = c.size() - 1; i >= 0; i--) {

cout << c[i];

}

puts("");

cout << r;

return 0;

}



/*

*乘法

*/

#include <iostream>

#include <vector>

using namespace std;

vector<int> mul(vector<int> &A, vector<int> &B) {

vector<int> C(A.size() + B.size());

for (int i = 0; i < A.size(); i++)

for (int j = 0; j < B.size(); j++)

C[i + j] += A[i] * B[j];

for (int i = 0, t = 0; i < C.size() || t; i++) {

t += C[i];

if (i >= C.size()) C.push_back(t % 10);

else C[i] = t % 10;

t /= 10;

}

while (C.size() > 1 && C.back() == 0) C.pop_back();

return C;

}

int main() {

string a, b;

cin >> a >> b;

vector<int> A, B;

for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');

for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');

vector<int> C = mul(A, B);

for (int i = C.size() - 1; i >= 0; i--) cout << C[i];

return 0;

}

 

1.6尺取法

/*

*尺取法 适用于l增大时侯r跟着增大取到一些合法的区间时使用

*/

int ans = INF, l = 1, r = 1, cnt = 0;

for(; ;)

{

while(r <= n && cnt < sum) cnt += a[r++]; //直到符合总数

if(cnt < sum) break; //再也无法满足

ans = min(ans, r - l); //更新答案

cnt -= a[l++]; //尝试进步

}

 

1.7归并排序

int n, a[N], b[N]; //b为辅助数组

void merge_dfs(int l, int mid, int r)

{

int i = l, j = mid + 1, k = l;

while(i <= mid && j <= r)

{

b[k++] = (a[i] < a[j] ? a[i++] : a[j++]); //排序

}

while(i <= mid) b[k++] = a[i++];

while(j <= r) b[k++] = a[j++];

RE(i, l, r) a[i] = b[i]; //还原

}

void dfs(int l, int r)

{

if(l == r) return;

int mid = l + r >> 1;

dfs(l, mid);

dfs(mid + 1, r);

merge_dfs(l, mid, r);

}

 

2. 数据结构

2.1单调栈

/*

*top = 0 栈空

*st[++top] = x 插入

*st[top] 栈顶

*/

int top, st[N]; //指针 数组栈

for(int i = 1; i <= n; ++i) {

if(top == 0) st[++top] = i;//为空入栈

//维护单调性 b[]存储答案

hile(top != 0 && a[i] > a[st[top]]) b[st[top]] = i, top--;

st[++top] = i;

}

 

 

2.2单调队列

/*

*head 队头 tail队尾

*head <= tail 队列不为空

*head++ 出队

*q[++tail] = x 入队

*/

int head, tail, q[N], idx[N];

head = 1, tail = 0;

for(int i = 1; i <= n; ++i) {

while(head <= tail && a[i] > q[tail]) tail--;

q[++tail] = a[i];

idx[tail] = i;

if(idx[head] <= i - k) head++;//队头元素过时

if(i >= k) cout << q[head] << " ";

}

 

2.3 ST表

struct rmq {

int f[N][50], lg[N] = {-1};

void init() {

for(int i = 1; i <= n; ++i) f[i][0] = a[i];

for(int i = 1; i <= n; ++i) lg[i] = lg[i >> 1] + 1;

for(int j = 1; j <= lg[n]; ++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]);

}

int query(int x, int y) {

int s = lg[y - x + 1];

return max(f[x][s], f[y - (1 << s) + 1][s]);

}

} st;

 

2.4树状数组

/*

*单点修改 区间查询

*/

int lowbit(int x) {

return x & -x;

}

void change(int i, int x) {

for(; i <= n; i += lowbit(i)) c[i] += x;

}

//查询[1, i],

int query(int i) {

int res = 0;

for(; i; i -= lowbit(i)) res += c[i];

return res;

}



/*

*单点查询 区间修改

*函数模板均跟第一个一样,读入a[]数组,以及区间修改时使用到差分

*/

add(i, a[i] - a[i - 1]); //初始化

cin >> l >> r >> x;

add(l, x), add(r + 1, -x); //差分更新





/*

*树状数组上二分求第k大:

*假设有n个数, 求第k大等价于求(n - k + 1)小

*可将权值用树状数组维护,二分查找第k大。

*/

scanf("%d", &k);

k = num - k + 1;//num是实际存在的数的个数

int l = 1, r = n;//值域

while(l < r) {

int mid = l + r >> 1;

if(query(mid) >= k) r = mid;

else l = mid + 1;

}

cout << l;

 

2.5线段树

2.5.1线段树结构

/*

*线段树主要考虑区间维护的信息

*以及如何设计lazy

*区间修改区间查询模板

*/

struct tnode {

int l, r; //区间边界

int sum, lazy; //需要维护的信息

};

struct segment_tree {

tnode t[N << 2];

void build(int k, int l, int r) { //建树

t[k].l = l, t[k].r = r;

if(l == r) {

t[k].sum = a[l];

return;

}

int mid = (l + r) >> 1;

build(k << 1, l, mid);

build(k << 1 | 1, mid + 1, r);

t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum; //push_up

}

void push_down(int k, int l, int r) { //下放懒标记

if(t[k].lazy) { //lazy值存在

int mid = l + r >> 1;

t[k << 1].lazy += t[k].lazy; //union_lazy

t[k << 1 | 1].lazy += t[k].lazy;

t[k << 1].sum += t[k].lazy * (mid - l + 1); //cal_lazy

t[k << 1 | 1].sum += t[k].lazy * (r - mid);

t[k].lazy = 0;

}

}

void change(int k, int l, int r, int val) {

if(t[k].l >= l && t[k].r <= r) {

t[k].lazy += val; //union_lazy

t[k].sum += (t[k].r - t[k].l + 1) * val; //cal_lazy

return;

}

push_down(k, t[k].l, t[k].r);

int mid = (t[k].l + t[k].r) >> 1;

if(r <= mid) change(k << 1, l, r, val);

else if(l > mid) change(k << 1 | 1, l, r, val);

else change(k << 1, l, mid, val), change(k << 1 | 1, mid + 1, r, val);

t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum; //push_up

}

int query(int k, int l, int r) {

if(t[k].l >= l && t[k].r <= r) {

return t[k].sum;

}

push_down(k, t[k].l, t[k].r);

int mid = t[k].l + t[k].r >> 1;

if(r <= mid) return query(k << 1, l, r);

else if(l > mid) return query(k << 1 | 1, l, r);

else return query(k << 1, l, mid) + query(k << 1 | 1, mid + 1, r);

}

};

 

2.5.2线段树优化建图

/*

*某点到区间建边,不需要虚点

*/

#include <bits/stdc++.h>

using namespace std;

const int N = 2e6 + 10;

int cnt, head[N];

struct edge {

int to, nex, w;

} e[N << 1];

inline void add(int u, int v, int w) {

e[++cnt].to = v;

e[cnt].w = w;

e[cnt].nex = head[u];

head[u] = cnt;

}

int n, m, s;

int tot, Ls[N], Rs[N], In, Out;

//入树 实现 u -> [L, R] 自上往下连边

void buildIn(int &k, int l, int r) {

if(l == r) {

k = l;

return;

}

k = ++tot;

int mid = l + r >> 1;

buildIn(Ls[k], l, mid);

buildIn(Rs[k], mid + 1, r);

add(k, Ls[k], 0);

add(k, Rs[k], 0);

}

//出树 实现 [L, R] -> u 自下往上连边

void buildOut(int &k, int l, int r) {

if(l == r) {

k = l;

return;

}

k = ++tot;

int mid = l + r >> 1;

buildOut(Ls[k], l, mid);

buildOut(Rs[k], mid + 1, r);

add(Ls[k], k, 0);

add(Rs[k], k, 0);

}

void addIn(int k, int l, int r, int L, int R, int tag, int w) {

if(l >= L && r <= R) {

add(tag, k, w);

return;

}

int mid = l + r >> 1;

if(L <= mid) addIn(Ls[k], l, mid, L, R, tag, w);

if(R > mid) addIn(Rs[k], mid + 1, r, L, R, tag, w);

}

void addOut(int k, int l, int r, int L, int R, int tag, int w) {

if(l >= L && r <= R) {

add(k, tag, w);

return;

}

int mid = l + r >> 1;

if(L <= mid) addOut(Ls[k], l, mid, L, R, tag, w);

if(R > mid) addOut(Rs[k], mid + 1, r, L, R, tag, w);

}

signed main() {

cin >> n >> m >> s; //n点m条边源点

tot = n;//线段树结点标号从n+1开始

buildIn(In, 1, n);

buildOut(Out, 1, n);

int op, l, r, u, v, w;

while(m-- && cin >> op) {

if(op == 1) {

cin >> u >> v >> w;

add(u, v, w);

} else if(op == 2) {

cin >> u >> l >> r >> w;

addIn(In, 1, n, l, r, u, w);

} else {

cin >> u >> l >> r >> w;

addOut(Out, 1, n, l, r, u, w);

}

}

dijstra();

return 0;

}



/*

*区间到区间建图,需要虚点p,q

*/

#include <bits/stdc++.h>

#define ll long long

using namespace std;

template <typename T> inline void read(T& t) {

int f = 0, c = getchar();

t = 0;

while (!isdigit(c)) f |= c == '-', c = getchar();

while (isdigit(c)) t = t * 10 + c - 48, c = getchar();

if (f) t = -t;

}

const int N = 5e6 + 10;

int cnt, head[N];

struct edge {

int to, nex, w;

} e[N << 1];

inline void add(int u, int v, int w) {

e[++cnt].to = v;

e[cnt].w = w;

e[cnt].nex = head[u];

head[u] = cnt++;

}

int n, m, s;

int tot, Ls[N], Rs[N], In, Out;

//入树 自上而下建边

void buildIn(int &k, int l, int r) {

if(l == r) {

k = l;

return;

}

k = ++tot;

int mid = l + r >> 1;

buildIn(Ls[k], l, mid);

buildIn(Rs[k], mid + 1, r);

add(k, Ls[k], 0);

add(k, Rs[k], 0);

}

//出树 自下而上建边

void buildOut(int &k, int l, int r) {

if(l == r) {

k = l;

return;

}

k = ++tot;

int mid = l + r >> 1;

buildOut(Ls[k], l, mid);

buildOut(Rs[k], mid + 1, r);

add(Ls[k], k, 0);

add(Rs[k], k, 0);

}

void addOut(int k, int l, int r, int L, int R, int idx) {

if(l >= L && r <= R) {

add(k, idx, 0);

return;

}

int mid = l + r >> 1;

if(L <= mid) addOut(Ls[k], l, mid, L, R, idx);

if(R > mid) addOut(Rs[k], mid + 1, r, L, R, idx);

}

void addIn(int k, int l, int r, int L, int R, int idx) {

if(l >= L && r <= R) {

add(idx, k, 0);

return;

}

int mid = l + r >> 1;

if(L <= mid) addIn(Ls[k], l, mid, L, R, idx);

if(R > mid) addIn(Rs[k], mid + 1, r, L, R, idx);

}

void insertt(int a, int b, int c, int d) {

int p = ++tot;

addOut(Out, 1, n, a, b, p);

int q = ++tot;

addIn(In, 1, n, c, d, q);

add(p, q, 1);

}

signed main() {

read(n), read(m), read(s);

tot = n;

buildIn(In, 1, n);

buildOut(Out, 1, n);

int a, b, c, d;

while(m--) {

read(a);

read(b);

read(c);

read(d);

insertt(a, b, c, d);

insertt(c, d, a, b);

}

dijstra();

return 0;

}

 

2.5.3维护GCD

#include <bits/stdc++.h>

#define go continue

#define int long long

#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

#define fory(i,a,b) for(int i = a; i <= b; ++i)

using namespace std;

const int N = 5e5 + 10;

int n, m, a[N];

inline int gcd(int a, int b) {

return b ? gcd(b, a % b) : a;

}

struct node {

int l, r, sum, d;

};

struct segment_tree {

node t[N << 2];

int mp[N];

inline void help_push(node& a, node& b, node& c) {

a.sum = b.sum + c.sum;

a.d = gcd(b.d, c.d);

}

inline void push_up(int root) {

int ch = root << 1;

help_push(t[root], t[ch], t[ch + 1]);

}

inline void build(int root, int l, int r) {

t[root].l = l;

t[root].r = r;

if(l != r) {

int ch = root << 1;

int mid = (l + r) >> 1;

build(ch, l, mid);

build(ch + 1, mid + 1, r);

push_up(root);

} else {

int tmp = a[l] - a[l - 1];

t[root].d = tmp;

t[root].sum = tmp;

mp[l] = root;

}

}

inline void change(int root, int x, int y) {

x = mp[x];

t[x].d = t[x].sum + y;

t[x].sum += y;



while(x >>= 1) push_up(x);

}

inline node query(int root, int l, int r) {

if(t[root].l >= l && t[root].r <= r) {

return t[root];

} else {

int ch = root << 1;

int mid = (t[root].l + t[root].r) >> 1;



if(mid >= r) return query(ch, l, r);

else if(l > mid) return query(ch + 1, l, r);

else {

node left = query(ch, l, mid);

node right = query(ch + 1, mid + 1, r);

node res;

help_push(res, left, right);

return res;

}

}

}

} st;

int l, r, x;

char op[2];

signed main() {

read(n), read(m);

fory(i, 1, n) read(a[i]);

st.build(1, 1, n);

while(m--) {

scanf("%s%lld%lld", op, &l, &r);

if(*op == 'C') {

read(x);

st.change(1, l, x);

if(r + 1 <= n) st.change(1, r + 1, -x);

} else {

node ll = st.query(1, 1, l);

node rr = {0, 0, 0, 0};

if(l + 1 <= r) rr = st.query(1, l + 1, r);

printf("%lld\n", abs(gcd(ll.sum, rr.d)));

}

}

return 0;

}

 

2.5.4区间第K小

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;

int cnt, root[N];

int sum[N << 5], lc[N << 5], rc[N << 5];

int n, m, a[N], b[N], p;

void build(int& t, int l, int r) {

t = ++cnt;

if(l == r) return;

int mid = l + r >> 1;

build(lc[t], l, mid);

build(rc[t], mid + 1, r);

}

int change(int pre, int l, int r) {

int now = ++cnt;

lc[now] = lc[pre];

rc[now] = rc[pre];

sum[now] = sum[pre] + 1;

if(l == r) return now;

int mid = l + r >> 1;

if(p <= mid) lc[now] = change(lc[now], l, mid);

else rc[now] = change(rc[now], mid + 1, r);

return now;

}

int query(int L, int R, int l, int r, int k) {

if(l == r) return l;

int mid = l + r >> 1;

int x = sum[lc[R]] - sum[lc[L]];

if(x >= k) return query(lc[L], lc[R], l, mid, k);

else return query(rc[L], rc[R], mid + 1, r, k - x);

}

signed main() {

ios::sync_with_stdio(false), cin.tie(0);

cin >> n >> m;

for(int i = 1; i <= n; ++i) {

cin >> a[i];

b[i] = a[i];

}

sort(b + 1, b + 1 + n);

int l, r, k, q;

q = unique(b + 1, b + 1 + n) - b - 1;

build(root[0], 1, q);

for(int i = 1; i <= n; ++i) {

p = lower_bound(b + 1, b + 1 + q, a[i]) - b;

root[i] = change(root[i - 1], 1, q);

}

while(m--) {

cin >> l >> r >> k;

cout << b[query(root[l - 1], root[r], 1, q, k)] << "\n";

}

return 0;

}

 

2.6 LCA

/*

*倍增法求LCA

*/

#include <bits/stdc++.h>

using namespace std;

const int N = 5e5 + 10;

int head[N], cnt;

struct node {

int to, nex;

} e[N << 1];

void add_edge(int u, int v) {

e[++cnt].to = v;

e[cnt].nex = head[u];

head[u] = cnt;

}

int dep[N], f[N][30], clg[N];

void dfs(int u, int fa) {

f[u][0] = fa;

dep[u] = dep[fa] + 1;

for(int i = 1; i <= clg[dep[u]]; ++i) {

f[u][i] = f[f[u][i - 1]][i - 1];

}

for(int i = head[u]; i; i = e[i].nex) {

if(e[i].to != fa) dfs(e[i].to, u);

}

}

int lca(int x, int y) {

if(dep[x] < dep[y]) swap(x, y);

while(dep[x] > dep[y]) x = f[x][clg[dep[x] - dep[y]] - 1];

if(x == y) return x;

for(int i = clg[dep[x]] - 1; i >= 0; --i) {

if(f[x][i] != f[y][i]) {

x = f[x][i];

y = f[y][i];

}

}

return f[x][0];

}

int n, m, s;

signed main() {

cin >> n >> m >> s;

int x, y;

for(int i = 1; i <= n - 1; ++i) {

cin >> x >> y;

add_edge(x, y);

add_edge(y, x);

}

for(int i = 1; i <= n; ++i) clg[i] = clg[i - 1] + (1 << clg[i - 1] == i);

dfs(s, 0);

while(m--) {

cin >> x >> y;

cout << lca(x, y) << "\n";

}

return 0;

}



/*

*树上任意两点距离

*/

#include <bits/stdc++.h>

using namespace std;

const int N = 4e4 + 100;

int cnt, head[N];

struct node {

int to, nex, w;

} e[N << 1];

void add_edge(int u, int v, int w) {

e[++cnt].to = v;

e[cnt].w = w;

e[cnt].nex = head[u];

head[u] = cnt;

}

int dep[N], clg[N], f[N][30], dis[N];

void dfs(int u, int fa) {

f[u][0] = fa;

dep[u] = dep[fa] + 1;

for(int i = 1; i <= clg[dep[u]]; ++i) {

f[u][i] = f[f[u][i - 1]][i - 1];

}

for(int i = head[u]; i; i = e[i].nex) {

int v = e[i].to;

if(v != fa) {

dis[v] = dis[u] + e[i].w;

dfs(v, u);

}

}

}

int lca(int x, int y) {

if(dep[x] < dep[y]) swap(x, y);

while(dep[x] > dep[y]) x = f[x][clg[dep[x] - dep[y]] - 1];

if(x == y) return x;

for(int i = clg[dep[x]] - 1; i >= 0; --i) {

if(f[x][i] != f[y][i]) {

x = f[x][i];

y = f[y][i];

}

}

return f[x][0];

}

//公式

int cal_dis(int u, int v) {

return dis[u] + dis[v] - 2 * dis[lca(u, v)];

}

int n, q, u, v, w;

signed main() {

for(int i = 1; i < N; ++i) clg[i] = clg[i - 1] + (1 << clg[i - 1] == i);

int t;

cin >> t;

while(t--) {

cnt = 0;

memset(head, -1, sizeof head);

dis[1] = dep[1] = 0;

cin >> n >> q;

for(int i = 1; i <= n - 1; ++i) {

cin >> u >> v >> w;

add_edge(u, v, w);

add_edge(v, u, w);

}

dfs(1, 0);

while(q-- && cin >> u >> v) cout << cal_dis(u, v) << "\n";

}

return 0;

}

 

2.7莫队

/*

*询问[l, r]之间有多少个不同的数字

*/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int n, m;

int a[N], pos[N], ans[N], cnt;

map<int, int> mp;

struct qnode {

int l, r, id;

} q[N];

void add(int n) {

if(mp[a[n]]) mp[a[n]]++;

else {

mp[a[n]] = 1;

cnt++;

}

}

void sub(int n) {

mp[a[n]]--;

if(mp[a[n]] == 0) cnt--;

}

signed main() {

ios::sync_with_stdio(false), cin.tie(0);

cin >> n;

int siz = sqrt(n);

for(int i = 1; i <= n; ++i) {

cin >> a[i];

pos[i] = i / siz;

}

cin >> m;

for(int i = 1; i <= m; ++i) {

cin >> q[i].l >> q[i].r;

q[i].id = i;

}

sort(q + 1, q + 1 + m, [](qnode x, qnode y) {

return pos[x.l] == pos[y.l] ? x.r < y.r : pos[x.l] < pos[y.l];

});

int l = 1, r = 0;

for(int i = 1; i <= m; ++i) {

while(q[i].l < l) add(--l);

while(q[i].r > r) add(++r);

while(q[i].l > l) sub(l++);

while(q[i].r < r) sub(r--);

ans[q[i].id] = cnt;

}

for(int i = 1; i <= m; ++i) cout << ans[i] << "\n";

return 0;

}



/*

*离线区间查询mex

*/

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 100;

int n, m, a[N], mp[N];

int val[N], cnt[N], L[N], R[N], pos[N], tot;

int ok[N];

struct qnode {

int l, r, id;

} q[N];

bool cmp(qnode& a, qnode& b) {

return (pos[a.l] == pos[b.l]) ? a.r < b.r : pos[a.l] < pos[b.l];

}

int Q() {

int idx = 1;

while(val[idx] == R[idx] - L[idx] + 1) idx++;

for(int i = L[idx]; i <= R[idx]; ++i) if(!cnt[i]) return i;

}

void add(int x) {

if(cnt[x] == 0) val[pos[x]]++;

cnt[x]++;

}

void sub(int x) {

if(cnt[x] == 1) val[pos[x]]--;

cnt[x]--;

}

signed main() {

read(n), read(m);

for(int i = 1; i <= n; ++i) {

read(a[i]);

if(a[i] > n) a[i] = n + 1;

}

int siz = sqrt(n);

for(int i = 0; i <= n; ++i) pos[i] = i / siz + 1;

for(int i = 0; i <= n; i += siz) {

L[++tot] = i, R[tot] = i + siz - 1;

}

for(int i = 1; i <= m; ++i) {

read(q[i].l), read(q[i].r);

q[i].id = i;

}

sort(q + 1, q + 1 + m, cmp);

int l = 1, r = 0;

for(int i = 1; i <= m; ++i) {

while(l < q[i].l) sub(a[l++]);

while(l > q[i].l) add(a[--l]);

while(r > q[i].r) sub(a[r--]);

while(r < q[i].r) add(a[++r]);

ok[q[i].id] = Q();

}

for(int i = 1; i <= m; ++i) printf("%d\n", ok[i]);

return 0;

}



/*

*带修莫队

*询问[L, R]多少种不同颜色

*修改某个位置的颜色

*/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int n, m, ans;

int a[N], ok[N], mp[N];

struct qnode {

int l, r, pre, id, bkl, bkr;

} q[N];

int qnum, cnum;

struct cnode {

int pos, val;

} c[N];

void change(int now, int i) {

if(c[now].pos >= q[i].l && c[now].pos <= q[i].r) {

if(--mp[a[c[now].pos]] == 0) ans--;

if(++mp[c[now].val] == 1) ans++;

}

swap(c[now].val, a[c[now].pos]);

}

void add(int x) {

if(++mp[x] == 1) ans++;

}

void sub(int x) {

if(--mp[x] == 0) ans--;

}

inline bool cmp(register qnode a, register qnode b) {

return a.bkl != b.bkl ? a.bkl < b.bkl : (a.bkr != b.bkr ? a.bkr < b.bkr : a.pre < b.pre);

}

void moqueue() {

int l = 1, r = 0, now = 0;

for(int i = 1; i <= qnum; ++i) {

while(l < q[i].l) sub(a[l++]);

while(l > q[i].l) add(a[--l]);

while(r < q[i].r) add(a[++r]);

while(r > q[i].r) sub(a[r--]);

while(now < q[i].pre) now++, change(now, i);

while(now > q[i].pre) change(now, i), now--;

ok[q[i].id] = ans;

}

for(int i = 1; i <= qnum; ++i) cout << ok[i] << "\n";

}

signed main() {

read(n), read(m);

int siz = pow(n, 2.0 / 3.0);

for(int i = 1; i <= n; ++i) {

read(a[i]);

}

char op[N];

while(m--) {

scanf("%s", op);

if(*op == 'Q') {

read(q[++qnum].l);

read(q[qnum].r);

q[qnum].pre = cnum;

q[qnum].id = qnum;

q[qnum].bkl = (q[qnum].l - 1) / siz + 1;

q[qnum].bkr = (q[qnum].r - 1) / siz + 1;

} else {

read(c[++cnum].pos);

read(c[cnum].val);

}

}

sort(q + 1, q + 1 + qnum, cmp);

moqueue();

return 0;

}

 

2.8树链剖分(重链)

/*

*1 x y z 树从 x 到 y 结点最短路径上所有节点的值都加上 z

*2 x y 求树从 x 到 y 结点最短路径上所有节点的值之和

*3 x z 表示将以 x 为根节点的子树内所有节点值都加上 z

*4 x 表示求以 x 为根节点的子树内所有节点值之和

*/

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;

int cnt, head[N];

struct edge {

int to, nex;

} e[N << 1];

inline void add_edge(int u, int v) {

e[++cnt].to = v;

e[cnt].nex = head[u];

head[u] = cnt;

}

int n, m, root, mod;

int a[N], w[N]; //初始点权, dfs序后的点权

int dfn[N], siz[N], son[N], f[N], top[N], dep[N], tim; //时间戳 子树大小 重儿子 父亲 当前链顶端节点

struct segment_tree {

struct tnode {

int l, r, sum, lazy;

};

tnode t[N << 2];

inline void push_down(int root) {

if(t[root].lazy != 0) {

t[root].sum += (t[root].lazy * (t[root].r - t[root].l + 1)) % mod;

if(t[root].l != t[root].r) {

int ch = root << 1;

t[ch].lazy += t[root].lazy;

t[ch + 1].lazy += t[root].lazy;

}

t[root].lazy = 0;

}

}

inline void push_up(int root) {

int ch = root << 1;

push_down(ch);

push_down(ch + 1);

t[root].sum = (t[ch].sum + t[ch + 1].sum) % mod;

}

inline void build(int root, int l, int r) {

t[root].l = l, t[root].r = r;

if(l != r) {

int ch = root << 1;

int mid = l + r >> 1;

build(ch, l, mid);

build(ch + 1, mid + 1, r);

push_up(root);

} else {

t[root].lazy = 0;

t[root].sum = w[l] % mod;

}

}

void change(int root, int l, int r, int k) {

push_down(root);

if(t[root].l >= l && t[root].r <= r) {

t[root].lazy += k;

return;

}

int ch = root << 1;

int mid = t[root].l + t[root].r >> 1;

if(r <= mid) change(ch, l, r, k);

else if(l > mid) change(ch + 1, l, r, k);

else change(ch, l, mid, k), change(ch + 1, mid + 1, r, k);

push_up(root);

}

int query(int root, int l, int r) {

push_down(root);

if(t[root].l >= l && t[root].r <= r) {

return t[root].sum % mod;

}

int ch = root << 1;

int mid = t[root].l + t[root].r >> 1;

if(r <= mid) return query(ch, l, r) % mod;

else if(l > mid) return query(ch + 1, l, r) % mod;

else return (query(ch, l, mid) % mod + query(ch + 1, mid + 1, r) % mod) % mod;

}

} st;

void dfs1(int u, int fa) {

f[u] = fa;

dep[u] = dep[fa] + 1;

siz[u] = 1;

int maxsize = -1;

for(int i = head[u]; i; i = e[i].nex) {

int v = e[i].to;

if(v == fa) continue;

dfs1(v, u);

siz[u] += siz[v];

if(siz[v] > maxsize) {

maxsize = siz[v];

son[u] = v;

}

}

}

void dfs2(int u, int t) {

dfn[u] = ++tim;

top[u] = t;

w[tim] = a[u];

if(!son[u]) return;

dfs2(son[u], t);

for(int i = head[u]; i; i = e[i].nex) {

int v = e[i].to;

if(v == f[u] || v == son[u]) continue;

dfs2(v, v);

}

}

inline void op1(int x, int y, int k) {

k %= mod;

while(top[x] != top[y]) {

if(dep[top[x]] < dep[top[y]]) swap(x, y);

st.change(1, dfn[top[x]], dfn[x], k);

x = f[top[x]];

}

if(dep[x] > dep[y]) swap(x, y);

st.change(1, dfn[x], dfn[y], k);

}

inline int op2(int x, int y) {

int cnt = 0;

while(top[x] != top[y]) {

if(dep[top[x]] < dep[top[y]]) swap(x, y);

cnt += st.query(1, dfn[top[x]], dfn[x]);

x = f[top[x]];

}

if(dep[x] > dep[y]) swap(x, y);

cnt += st.query(1, dfn[x], dfn[y]) % mod;

return cnt % mod;

}

inline void op3(int x, int k) {

st.change(1, dfn[x], dfn[x] + siz[x] - 1, k);

}

inline int op4(int x) {

return st.query(1, dfn[x], dfn[x] + siz[x] - 1);

}



signed main() {

ios::sync_with_stdio(false), cin.tie(0);

cin >> n >> m >> root >> mod;

for(int i = 1; i <= n; ++i) cin >> a[i];

int x, y;

for(int i = 1; i < n; ++i) {

cin >> x >> y;

add_edge(x, y);

add_edge(y, x);

}

dfs1(root, root);

dfs2(root, root);

st.build(1, 1, n);

while(m--) {

int op, z;

cin >> op;

if(op == 1) {

cin >> x >> y >> z;

op1(x, y, z);

} else if(op == 2) {

cin >> x >> y;

cout << op2(x, y) << "\n";

} else if(op == 3) {

cin >> x >> y;

op3(x, y);

} else {

cin >> x;

cout << op4(x) << "\n";

}

}

return 0;

}

 

2.9 dsu on tree

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int cnt, head[N];

struct E {

int to, nex;

} e[N << 1];

inline void add_edge(int u, int v) {

e[++cnt].to = v, e[cnt].nex = head[u], head[u] = cnt;

}

int siz[N], son[N];

void dfs(int u, int fa) {

siz[u] = 1;

for(int i = head[u]; i; i = e[i].nex) {

int v = e[i].to;

if(v == fa) continue;

dfs(v, u);

siz[u] += siz[v];

if(siz[v] > siz[son[u]]) son[u] = v;

}

}

int a[N], ok[N], cot[N], sum;

void cal(int u, int fa, int val) { //统计函数

cot[a[u]] += val;

if(val == 1 && cot[a[u]] == 1) sum++;

if(val == -1 && cot[a[u]] == 0) sum--;

for(int i = head[u]; i; i = e[i].nex) {

int v = e[i].to;

if(v != fa) cal(v, u, val);

}

}

void dsu(int u, int fa, bool flag) {

for(int i = head[u]; i; i = e[i].nex) {

int v = e[i].to;

if(v != fa && v != son[u]) dsu(v, u, true);//往轻儿子方向走

}

if(son[u] != 0) dsu(son[u], u, false);//往重儿子方向走

cot[a[u]]++;

if(cot[a[u]] == 1) sum++;//计算自己的贡献

for(int i = head[u]; i; i = e[i].nex) { //计算轻儿子的贡献

int v = e[i].to;

if(v != fa && v != son[u]) cal(v, u, 1);

}

ok[u] = sum;

if(flag) cal(u, fa, -1);//释放掉cot数组 清空其他信息

}

int n, m;

signed main() {

ios::sync_with_stdio(false), cin.tie(0);

cin >> n;

for(int i = 1, u, v; i < n; ++i) {

cin >> u >> v;

add_edge(u, v), add_edge(v, u);

}

for(int i = 1; i <= n; ++i) cin >> a[i];

dfs(1, 0);

dsu(1, 0, 0);

cin >> m;

int x;

while(m-- && cin >> x) cout << ok[x] << "\n";

return 0;

}

 

2.10 字典树

struct {

int cnt, son[26];

}c[N];

int idx;

inline void insertt(string& s) {

int p = 0;

for(auto x : s) {

x -= 'a';

if(!c[p].son[x]) c[p].son[x] = ++idx;

p = c[p].son[x];

++c[p].cnt;

}

}



inline int query(string& s) {

int p = 0, ok = 0;

for(auto x : s) {

x -= 'a';

if(!c[p].son[x]) return 0;

p = c[p].son[x];

}

return c[p].cnt;

}

 

3. 数论

 

3.1质数

/*

*质数:严格大于1 的整数,只有自己和1两个因子。

*算术基本定理:任何一个大于 1的自然数N,N不为质数,可以唯一被分解有

*限个质数的乘积:N = p1^a1 * p2^a2 * .. * pn^an

*且最多有一个大于sqrt(n)的质因子

*试除法判断质数:o(sqrt(n))

*/

bool is_prime(int x) {

if (x < 2) return false;

for (int i = 2; i <= x / i; i ++ )

if (x % i == 0)

return false;

return true;

}

/*

*分解质因数

*/

void divide(int x) {

for (int i = 2; i <= x / i; i ++ )

if (x % i == 0) {

int s = 0;

while (x % i == 0) x /= i, s++;

cout << i << ' ' << s << endl;

}

if (x > 1) cout << x << ' ' << 1 << endl;

cout << endl;

}

/*

***线性筛法**

*/

int primes[N], cnt;

bool st[N];

void get_primes(int n) {

for (int i = 2; i <= n; ++i) {

if (!st[i]) primes[cnt++] = i;

for (int j = 0; primes[j] <= n / i; ++j) {

st[primes[j] * i] = true;

if (i % primes[j] == 0) break;

}

}

}

 

3.2约数

/*

*一个数的约数集合

*/

vector<int> get_divisors(int x) {

vector<int> res;

for (int i = 1; i <= x / i; ++i)

if (x % i == 0) {

res.push_back(i);

if (i != x / i) res.push_back(x / i);

}

sort(res.begin(), res.end());

return res;

}

/*

*约数个数:

*由算术基本定理、组合数原理

*N = p1^a1 + p2^a2 + .. + pn^an

*设K为约数,sum为约数个数

*K = p1^b1 * p2^b2 * ... * pn ^bn (0 <= bi <= ai)

*sum = (a1 + 1) * (a2 + 1) * .. * (an + 1)

*/

//多个数相乘得到的约数个数,统计指数最后再计算即可

unordered_map<int, int> primes;

while (n--) {

int x;

cin >> x;

for (int i = 2; i <= x / i; ++i)

while (x % i == 0) {

x /= i;

primes[i]++;

}

if (x > 1) primes[x]++;

}

LL res = 1;

for (auto p : primes) res = res * (p.second + 1) % mod;

cout << res << endl;

/*

*约数之和:

*(p1^0 + p1^1 + ... + p1^a1) * (p2^0 + p2^1 + ... + p2^a2) * ...

*(pn^0 + pn^1 + ... + pn^an)

*/

unordered_map<int, int> primes;

while (n--) {

int x;

cin >> x;

for (int i = 2; i <= x / i; i++)

while (x % i == 0) {

x /= i;

primes[i]++;

}

if (x > 1) primes[x]++;

}

LL res = 1;

for (auto p : primes) {

LL a = p.first, b = p.second;

LL t = 1;

while (b--) t = (t * a + 1) % mod;

res = res * t % mod;

}

cout << res << endl;

 

3.3组合数

/*

*打表方式组合数,数据范围较小

*c[i][j] 代表i个里面选j个

*/

int c[N][N]; //c[i][j] 代表i个里面选j个

inline void init(){

for(int i = 0; i < N; ++i)

for(int j = 0; j <= i; ++j){

if(!j) c[i][j] = 1;

else c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;

}

}



/*

*数据范围预处理on到1e7,计算o1

*/

const int N = 2e5 + 10;

const int mod = 1e9 + 7;

int fac[N], inv[N];

int qk(int a, int b) {

int ans = 1;

while(b) {

if(b & 1) ans = ans * a % mod;

a = a * a % mod;

b >>= 1;

}

return ans;

}

void init() {

fac[0] = 1;

for(int i = 1; i < N; ++i) fac[i] = fac[i - 1] * i % mod;

inv[N - 1] = qk(fac[N - 1], mod - 2);

for(int i = N - 2; i >= 0; --i) inv[i] = inv[i + 1] * (i + 1) % mod;

}

int C(int a, int b){

if(b < 0 || b > a) return 0;

return fac[a] * inv[a - b] % mod * inv[b] % mod; //组合数定义

}



/*

*卢卡斯定理 适合n, m1e18级别, p1e5

*时间复杂度20*N*log(p)N*lgP

*/

int n, m, p;

int qk(int a, int b, int mod) {

int res = 1;

while(b) {

if(b & 1) res = res * a % mod;

a = a * a % mod;

b >>= 1;

}

return res;

}

int C(int a, int b, int p) {

int res = 1;

for(int i = 1, j = a; i <= b; i++, j--) {

res = res * j % p;

res = res * qk(i, p - 2, p) % p;

}

return res;

}

int lucas(int a, int b, int p) {

if(a < p && b < p) return C(a, b, p);

return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;

}

 

3.3欧拉函数

/*

f(n) : 1 - n 中与n互质的个数(gcd(a, b) = 1)

n = p1^a1.p2^a2...pn^an

f(n) = n * (1 - 1/p1)(1 - 1/p2)...(1 - 1/pn)

*/

int div_ola(int x) {

int res = x;

for(int i = 2; i <= x / i; ++i) {

if(x % i == 0) {

res = res / i * (i - 1);

while(x % i == 0) x /= i;

}

}

if(x > 1) res = res / x * (x - 1);

return res;

}

/*

*筛法

*/

const int N = 1e5 + 10;

int cnt, p[N], st[N], e[N];

void div_ola(int n) {

e[1] = 1;

for(int i = 2; i <= n; ++i) {

if(!st[i]) {

p[cnt++] = i;

e[i] = i - 1;

}

for(int j = 0; p[j] <= n / i; ++j) {

int t = p[j] * i;

st[t] = true;

if(i % p[j] == 0) {

e[t] = e[i] * p[j];

break;

}

e[t] = e[i] * (p[j] - 1);

}

}

}

signed main() {

div_ola(100);

for(int i = 1; i <= 100; ++i) cout << e[i] << " ";

return 0;

}

 

3.4SG函数

/*

*打表版本

*/

int sg[N], vis[N], f[N];

void cal_sg(int n) {

for(int i = 1; i <= n; ++i) { //从1开始 sg[0] = 0

memset(vis, 0, sizeof vis); //清空后继状态

for(int j = 1; f[j] <= i; ++j) { //f[i]保证有序且预处理的最大权值>=n

vis[sg[i - f[j]]] = 1;

}

for(int j = 0; ; ++j) if(!vis[j]) {

sg[i] = j;

break;

}

}

}



/*

*dfs版本 用于不好打表的情况

*/

int sg[N][N], f[N];

int dfs(int x) {

if(sg[x]) return sg[x];

int vis[N] = {0};

for(int i = 0; i < n; ++i) { //种类数

if(x >= f[i]) { //如果合法

dfs(x - f[i]);

vis[sg[x - f[i]] = 1

}

}

for(int i = 0; ; ++i) if(!vis[i]) {

return sg[x] = i;

}

}

 

 

4. 动态规划

4.1背包问题

4.1.1 01背包

/*

*01背包基础写法

*/

for(int i = 1; i <= n; ++i) {

for(int j = 1; j <= m; ++j) {

if(j >= v[i]) f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);

else f[i][j] = f[i - 1][j];

}

}

/*

*回滚优化 优化空间

*/

for(int i = 1; i <= n; ++i) {

for(int j = m; j >= v[i]; --j) { //一定是从后往前跑

f[j] = max(f[j], f[j - v[i]] + w[i]);

}

}

/*

*有些01变形不好使用回滚,再加个辅助数组帮助优化空间

*/

for(int i = 1; i <= n; ++i) {

for(int j = 1; j <= m; ++j) {

if(j < v[i]) f[j][0] = f[j][1];

else f[j][0] = max(f[j][1], f[j - v[i]][1] + w[i]);

}

for(int j = 1; j <= m; ++j) f[j][1] = f[j][0];

}

cout << f[m][0];

/*

*求刚好装满的最大值,初始化均为负值,f[0] = 0

*/

for(int i = 0; i < mx; i++) f[i] = -inf;//一个极小负值

f[0] = 0;

for(int i = 1; i <= n; i++) {

cin >> v[i] >> w[i];

}

for (int i = 1; i <= n; i++) {

for (int j = m; j >= v[i]; j--) {

f[j] = max(f[j], f[j - v[i]] + w[i]);

if (f[j] < 0) f[j] = -inf;

}

}

if(f[m] > 0) cout << f[m];

else cout << "no";

/*

*求n个物品,具有重量和价值,价值max,不超过背包容量的方案总数。

*/

cin >> n >> m;

for(int i = 0; i <= m; ++i) cnt[i] = 1;

for(int i = 1; i <= n; ++i)

cin >> v >> w;

for(int j = m; j >= v; --j) {

int val = f[j - v] + w;

if(val > f[j]) { //当前值更大,替换最优方案数

f[j] = val;

cnt[j] = cnt[j - v];

} else if(val == f[j]) cnt[j] = (cnt[j] + cnt[j - v]) % mod;

//出现了一个相同的最大值,叠加

}

}

cout << cnt[m];

/*

*n个整数,从中任意选出若干个组成m的方案数

*/

f[0] = 1;

int x;

for(int i = 1; i <= n; ++i) {

cin >> x;

for(int j = m; j >= x; --j) f[j] += f[j - x];

}

cout << f[m];

 

4.1.2完全背包

/*

*物品可重复选取

*/

for(int i = 1; i <= n; ++i) {

cin >> v >> w;

for(int j = v; j <= m; ++j) { //正着跑

f[j] = max(f[j], f[j - v] + w);

}

}

cout << f[m];

/*

*方案数

*/

f[0] = 1;

int x;

for(int i = 1; i <= n; ++i) {

cin >> x;

for(int j = x; j <=m; ++j) f[j] += f[j - x];

}

cout << f[m];

 

4.1.3多重背包

/*

*每种物品数量不定,二进制枚举优化

*/

for(int i = 1; i <= n; ++i) {

cin >> a >> b >> s;

int k = 1;

while(s - k >= 0) {

s -= k;

v[++cnt] = a * k;

w[cnt] = b * k;

k <<= 1;

}

if(s > 0) {

v[++cnt] = a * s;

w[cnt] = b * s;

}

}

n = cnt;

for(int i = 1; i <= n; ++i) {

for(int j = m; j >= v[i]; --j) {

f[j] = max(f[j], f[j - v[i]] + w[i]);

}

}

cout << f[m];

 

 

4.1.4分组背包

/*

*每组物品只能选一种

*/

for(int i = 1; i <= n; ++i) {

cin >> s[i];

for(int j = 1; j <= s[i]; ++j) cin >> v[i][j] >> w[i][j];

}

for(int i = 1; i <= n; ++i) {

for(int j = m; j > 0; --j) {

for(int k = 1; k <= s[i]; ++k) {

if(v[i][k] <= j)

f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);

}

}

}

cout << f[m];

 

4.2数位DP

int dfs(int pos, bool limit, bool lead)

{

if (pos == cnt)

return 1;

int ans = 0;

if (dp != -1)return dp;

for (int v = 0; v <= (limit ? A[pos] : 9); v++) // 根据是否limit决定循环上界

{

ans += dfs(pos + 1, limit && v == A[pos], lead && v == 0);

}

return dp = ans;

}

int f(int x) {

if (x == 0)return 0;

cnt = 0;

memset(A, 0, sizeof(A));

memset(dp, -1, sizeof(dp)); // 初始化dp数组为-1

while (x)

A[cnt++] = x % 10, x /= 10;

reverse(A, A + cnt);

return dfs(0, true, true);

}

 

4.3区间DP

f[i][j]: [i, j]区间的dp值

len = (n << 1)

rep(L, 2, n) { //长度

for(int i = 1; i <= len - L + 1; ++i) { //起点

int j = i + L - 1; //终点

for(int k = i; k < j; ++k) { //枚举中点

//更新

}

//更新

}

}

 

5. 图论

5.1最短路

/*

*弗洛伊德多源点最短路

*/

RE(i, 1, n)

RE(j, 1, n) {

g[i][j] = i == j ? 0 : INF;

}

RE(k, 1, n)

RE(i, 1, n)

RE(j, 1, n) {

g[i][j] = min(g[i][j], g[i][k] + g[k][j]);

}

/**\

单源最短路

输入: n m s t 接下来m行 u, v, w表示u -> v 有一条权值为w的无向边

input:

3 3 1 2

1 2 3

2 3 4

1 3 5

output:

3

\**/

\#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

struct node {

int to, w, next;

} e[N << 1];

int cnt = 0, head[N];

inline void add_edge(int u, int v, int w) {

e[++cnt].to = v;

e[cnt].w = w;

e[cnt].next = head[u];

head[u] = cnt;

}

inline void init() {

cnt = 0;

memset(head, -1, sizeof head);

}

int n, m, s, t;

struct v {

int x, dis;

bool operator < (const v& a) const {

return dis > a.dis; //stl默认大顶堆

}

};

int dis[N];

bool vis[N];

/**\

dijstra

1、从源点开始每次选取一个离点集距离最近的点t 添加到集合中

2、利用t点对集合中的点进行松弛操作,进行更新

3、使用堆进行1找点的操作降低时间复杂度

4、不能在有负边权的图中使用

\**/

const int inf = 2147483647;

int cnt, head[N];

struct E {

int to, nex, w;

}e[N << 1];

void add(int u, int v, int w) {

e[++cnt].nex = head[u], e[cnt].w = w, e[cnt].to = v, head[u] = cnt;

}

int n, m, s;

int dis[N];

bool vis[N];

void dst(int s) {

for(int i = 1; i <= n; ++i) dis[i] = inf;

dis[s] = 0;

using pii = pair<int, int>;

priority_queue<pii, vector<pii >, greater<pii >> q;

q.push({0, s});

while(q.size()) {

int u = q.top().second;

q.pop();

if(vis[u]) continue;

vis[u] = 1;

for(int i = head[u]; i; i = e[i].nex) {

int v = e[i].to;

if(dis[v] > dis[u] + e[i].w) {

dis[v] = dis[u] + e[i].w;

q.push({dis[v], v});

}

}

}

for(int i = 1; i <= n; ++i) cout << dis[i] << " ";

}

/**\

spfa算法思路:

1、每次迭代,取出队头点v,依次枚举从v出发的边,v->u,设边的长度为w

判断dis[v] + w 是否小于dis[u], 小于则更新值,

2、由于s-u的距离变短了,有可能u能改变其他点,使用vis数组判断是否在队列,没有则放入

3、若一个点的入队次数超过n,则存在负环

\**/

queue<int> q1;

inline void spfa(int s, int t) {

memset(dis, 0x3f, sizeof dis);

memset(vis, 0, sizeof vis);

dis[s] = 0;

vis[s] = 1;

q1.push(s);

while(!q1.empty()) {

int x = q1.front();

q1.pop();

vis[x] = 0;

for(int i = head[x]; i != -1; i = e[i].next) {

int y = e[i].to;

if(dis[y] > dis[x] + e[i].w) {

dis[y] = dis[x] + e[i].w;

if(!vis[y]) {

q1.push(y);

vis[y] = 1;

}

}

}

}

if(dis[t] >= 0x3f3f3f3f) cout << -1;

else cout << dis[t];

}

inline void solve() {

init();

cin >> n >> m >> s >> t;

for(int i = 1; i <= m; ++i) {

int x, y, z;

cin >> x >> y >> z;

add_edge(x, y, z);

add_edge(y, x, z);

}

//dijstra(s, t);

spfa(s, t);

}

signed main() {

solve();

return 0;

}

 

5.2拓扑排序

/**\

拓扑排序:每次找到入度为0的点删掉

判断一个有向图中是否有环,无环的图都能进行拓扑排序

\**/

const int N = 1e5 + 10;

int inr[N]; //记录入度的数组

struct node {

int t, next;

} e[N << 1];

int cnt = 0;

int head[N];

int n, m;

void add_edge(int x, int y) {

e[++cnt].t = y;

e[cnt].next = head[x];

head[x] = cnt;

}

void topo() {

queue<int> q;

for(int i = 1; i <= n; ++i) {

if(inr[i] == 0) q.push(i);

}

int idx = 0, ans[N];

while(!q.empty()) {

int x = q.front();

ans[++idx] = x;

q.pop();

for(int i = head[x]; i != -1; i = e[i].next) {

inr[e[i].t]--;

if(inr[e[i].t] == 0) q.push(e[i].t);

}

}

if(idx != n) cout << -1;

else {

for(int i = 1; i <= idx; ++i) cout << ans[i] << " ";

}

}

signed main() {

scanf("%d %d", &n, &m);

memset(head, -1, sizeof head);

for(int i = 1; i <= m; ++i) {

int x, y;

scanf("%d %d", &x, &y);

inr[y]++;

add_edge(x, y);

}

topo();

return 0;

}

 

 

 

5.3二分图

/*

*染色法判断二分图

*二分图性质:至少两个点,回路长度均为偶数

*/

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int cnt, head[N];

struct edge {

int to, nex;

} e[N << 1];

inline void add_edge(int u, int v) {

e[++cnt].to = v;

e[cnt].nex = head[u];

head[u] = cnt;

}

int n, m, col[N];

vector<int> v1, v2;

bool dfs(int u) {

for(int i = head[u]; i; i = e[i].nex) {

int v = e[i].to;

if(!col[v]) {

col[v] = 3 - col[u];

if(!dfs(v)) return false;

}

if(col[v] == col[u]) return false;

}

return true;

}

signed main() {

ios::sync_with_stdio(false), cin.tie(0);

cin >> n >> m;

int u, v;

for(int i = 0; i < m; ++i) {

cin >> u >> v;

add_edge(u, v);

add_edge(v, u);

}

for(int i = 1; i <= n; ++i) {

if(col[i]) continue;

if(!col[i]) {

col[i] = 1;

if(!dfs(i)) {

cout << "-1\n";

return 0;

}

}

}

for(int i = 1; i <= n; ++i) {

if(col[i] == 1) v1.push_back(i);

else v2.push_back(i);

}

cout << v1.size() << "\n";

for(int i = 0; i < v1.size(); ++i) cout << v1[i] << " \n"[i == v1.size() - 1];

cout << v2.size() << "\n";

for(int i = 0; i < v2.size(); ++i) cout << v2[i] << " \n"[i == v2.size() - 1];

return 0;

}



/**\

https://www.luogu.com.cn/problem/P3386

二分图最大匹配匈牙利算法:

假设左部是男孩,右部是女孩

1、对于一个男孩子,如果他喜欢女孩y,女孩y没有配偶,就在一起

2、如果y有配偶了,x还是想去挖一手墙脚,y也不是什么好女人,

如果y发现他的配偶z能换一个对象(这里dfs),y就主动抛弃z(z能换一个对象),跟x在一起

这样就多了一对配偶

\**/

#include <bits/stdc++.h>

using namespace std;

const int N = 1010;

int n, m, e;//左部 右部 边数

int g[N][N], vis[N], link[N];

//dfs搜索的起点是左集合, link[]代表右集合

int dfs(int x) {

for(int i = 1; i <= m; ++i) {

if(!g[x][i] || vis[i]) continue; //如果没有边 或者已经被邀请过了 就跳过

vis[i] = 1;

if(link[i] == 0 || dfs(link[i])) { //如果没有配偶,或者能让自己的配偶找到配偶(找到增广路),就在一起

link[i] = x;

return 1;

}

}

return 0;

}

signed main() {

int ok = 0;

cin >> n >> m >> e;

for(int i = 1, x, y; i <= e; ++i) {

cin >> x >> y;

g[x][y] = 1;

}

for(int i = 1; i <= n; ++i) {

memset(vis, 0, sizeof vis);

if(dfs(i)) ok++;//如果能匹配成功,边数++

}

printf("%lld\n", ok);

return 0;

}



/*

*二分图最大权完美匹配

*/

#include<bits/stdc++.h>

using namespace std;

int n, m; //左右点均为n m条边

long long edge[505][505];

int matched[505];//记录右部点所匹配的左部点

int visy[505];

int lx[505], ly[505]; //lx表示左部点的顶标,ly表示右部点的顶标

int pre[505];

long long slack[505];

void bfs(int want_match) {

int now, new_match = 0;

int x = 0; //0点初始化

memset(pre, 0, sizeof pre); //重新建立交错树(因为每一次要重新找增广路)

memset(slack, 0x3f, sizeof slack); //初始化松弛量

matched[x] = want_match; //初始化0点匹配到了u这个左部点

do {

now = matched[x]; //找到当前寻找匹配的左部点

long long delta = 1e18; //delta记录寻找的增广路路径的松弛量

visy[x] = true;

//标记本轮已经在这个点找过增广路,和匈牙利算法中vis数组同理

for(register int i = 1; i <= n; i++) { //枚举右部点

if(!visy[i]) { //找到了一个还没有尝试找增广路的右部点

if(slack[i] > lx[now] + ly[i] - edge[now][i]) {

//找到了一个匹配点

//优化DFS进行的find操作

slack[i] = lx[now] + ly[i] - edge[now][i];

pre[i] = x; //记录交错树(BFS对DFS的优化)

}

if(slack[i] < delta) {

delta = slack[i]; //更新delta

new_match = i;

}

}

}

for(register int i = 0; i <= n; i++) { //因为初始化的是0,所以说必须从0开始修改顶标

if(visy[i]) {

lx[matched[i]] -= delta; //i点匹配到的左部点的顶标减去delta

ly[i] += delta; //i点的顶标加上delta

//和DFS版本同理

} else slack[i] -= delta; //进行松弛操作

}

x = new_match; //下一个访问的左部点即刚才取出的右部点的匹配点

//此处x指代找到的右部点

} while(matched[x]);

while(x) matched[x] = matched[pre[x]], x = pre[x]; //交错树还原操作

}

long long KM() {

memset(matched, 0, sizeof matched);

memset(lx, 0, sizeof lx);

memset(ly, 0, sizeof ly);

for(register int i = 1; i <= n; i++) {

memset(visy, 0, sizeof visy); //每次重新进行增广

bfs(i);

}

long long ans = 0;

for(register int i = 1; i <= n; i++)

ans += (long long)edge[matched[i]][i]; //正常的统计答案

return ans;

}

int main() {

n = read(), m = read();

memset(edge, -0x3f, sizeof edge); //赋初值为负无穷,因为题目边权有负数

for(register int i = 1; i <= m; i++) {

int u = read(), v = read(), w = read();

edge[u][v] = max(edge[u][v], (long long)w);

}

printf("%lld\n", KM());

for(register int i = 1; i <= n; i++)

printf("%d ", matched[i]);

return 0;

}

 

5.4网络流

5.4.1最大流dinic

#include <bits/stdc++.h>

using namespace std;

const int N = 1e4 + 10, M = 2e5 + 10;

int n, m, S, T;

int head[N], ver[M], edge[M], Next[M], idx;

int deep[N], cur[M];

void add(int a, int b, int c) {

ver[idx] = b, edge[idx] = c, Next[idx] = head[a], head[a] = idx++;

ver[idx] = a, edge[idx] = 0, Next[idx] = head[b], head[b] = idx++;

}

bool bfs() {

queue<int> q;

memset(deep, 0, sizeof deep);

q.push(S);

cur[S] = head[S];

deep[S] = 1;

while (q.size()) {

int t = q.front();

q.pop();

for (int i = head[t]; i != -1; i = Next[i]) {

int e = ver[i];

if (deep[e] == 0 && edge[i]) {

deep[e] = deep[t] + 1;

cur[e] = head[e];

if (e == T)

return 1;

q.push(e);

}

}

}

return 0;

}

int find(int fa, int limit) {

if (fa == T)

return limit;

int flow = 0;

for (int i = cur[fa]; i != -1 && flow < limit; i = Next[i]) {

cur[fa] = i;

int e = ver[i];

if (deep[e] == deep[fa] + 1 && edge[i]) {

int t = find(e, min(edge[i], limit - flow));

if (!t) {

deep[e] = 0;

continue;

}

edge[i] -= t, edge[i ^ 1] += t, flow += t;

}

}

return flow;

}



int dinic() {

int maxflow = 0, flow;

while (bfs())

while (flow = find(S, INT_MAX))

maxflow += flow;

return maxflow;

}

int main() {

cin >> n >> m >> S >> T;

memset(head, -1, sizeof head);

while (m--) {

int a, b, c;

cin >> a >> b >> c;

add(a, b, c);

}

cout << dinic();

}

 

5.4.2最小费用最大流

#include <bits/stdc++.h>

using namespace std;

const int N = 5e3 + 10, M = 1e5 + 10;

int n, m, S, T;

int head[N], ver[M], edge[M], w[M], Next[M], idx;

int dist[N], pre[N], incf[N];

bool vis[N];

void add(int a, int b, int c, int d) {

ver[idx] = b, edge[idx] = c, w[idx] = d, Next[idx] = head[a], head[a] = idx++;

ver[idx] = a, edge[idx] = 0, w[idx] = -d, Next[idx] = head[b], head[b] = idx++;

}

bool spfa() {

queue<int> q;

memset(dist, 0x3f, sizeof dist);

memset(incf, 0, sizeof incf);

incf[S] = INT_MAX, dist[S] = 0;

q.push(S);

while (q.size()) {

int t = q.front();

q.pop();

vis[t] = false;

for (int i = head[t]; i != -1; i = Next[i]) {

int e = ver[i];

if (edge[i] && dist[e] > dist[t] + w[i]) {

dist[e] = dist[t] + w[i];

pre[e] = i;

incf[e] = min(edge[i], incf[t]);

if (!vis[e]) {

q.push(e);

vis[e] = 1;

}

}

}

}

return incf[T] > 0;

}

void EK(int &flow, int &cost) {

flow = cost = 0;

while (spfa()) {

int t = incf[T];

flow += t, cost += t * dist[T];

for (int i = T; i != S; i = ver[pre[i] ^ 1]) {

edge[pre[i]] -= t;

edge[pre[i] ^ 1] += t;

}

}

}

signed main() {

cin >> n >> m >> S >> T;

memset(head, -1, sizeof head);

while (m--) {

int a, b, c, d;

cin >> a >> b >> c >> d;

add(a, b, c, d);

}

int flow, cost;

EK(flow, cost);

cout << flow << " " << cost;

}

 

6. 字符串

长度为n的字符串

1、有n(n+1)/2 +1个子串;

2、非空子串:n(n+1)/2;

3、非空真子串:n(n+1)/2– 1。

6.5哈希

/*

*双哈希统计多少个相同的字符串 可以利用ull的自动取模

*/

#define ull unsigned long long

const int N = 1e5 + 10;

const int mod = 1e9 + 7;

const int P = 131, PP = 1331;

ull p[N], pp[N], hs1[N], hs2[N];

char s[N];

set<pair<ull, ull> > st;

ull get1(int l, int r) {

return (hs1[r] - hs1[l - 1] * p[r - l + 1] % mod + mod) % mod;

}

ull get2(int l, int r) {

return (hs2[r] - hs2[l - 1] * pp[r - l + 1] % mod + mod) % mod;

}

void cal_hash(int l, int r) {

ull t1 = get1(l, r), t2 = get2(l, r);

st.insert({t1, t2});

}

signed main() {

int T;

scanf("%d", &T);

while(T-- && scanf("%s", s + 1)) {

int n = strlen(s + 1);

for(int i = 1; i <= n; ++i) {

p[i] = (p[i - 1] * P) % mod;

pp[i] = (pp[i - 1] * PP) % mod;

hs1[i] = (hs1[i - 1] * P % mod + s[i]) % mod;

hs2[i] = (hs2[i - 1] * PP % mod + s[i]) % mod;

}

cal_hash(1, n);

}

printf("%d\n", st.size());

return 0;

}



/*

*n,m的矩阵,查询有q个大小a,b的矩阵出现

*/

#define ull unsigned long long

using namespace std;

const int N = 1e3 + 10;

char ch;

ull h[N][N];

ull p[N], pp[N];

unordered_map<ull, int> mp;

ull get(int x1, int y1, int x2, int y2) {

return h[x2][y2] - h[x2][y1 - 1] * p[y2 - y1 + 1] - h[x1 - 1][y2] * pp[x2 - x1 + 1]

\+ h[x1 - 1][y1 - 1] * p[y2 - y1 + 1] * pp[x2 - x1 + 1];

}

int n, m, a, b, q;

signed main() {

cin >> n >> m >> a >> b;

p[0] = pp[0] = 1;

for(int i = 1; i <= 1000; ++i) {

p[i] = p[i - 1] * 131;

pp[i] = pp[i - 1] * 233;

}

for(int i = 1; i <= n; ++i)

for(int j = 1; j <= m; ++j) {

cin >> ch;

h[i][j] = h[i][j - 1] * 131 + ch;

}



for(int i = 1; i <= n; ++i)

for(int j = 1; j <= m; ++j) {

h[i][j] = h[i][j] + h[i - 1][j] * 233;

}

for(int i = 1; i <= n - a + 1; ++i)

for(int j = 1; j <= m - b + 1; ++j) {

ull z = get(i, j, i + a - 1, j + b - 1);

mp[z]++;

}

cin >> q;

while(q--) {

for(int i = 1; i <= a; ++i)

for(int j = 1; j <= b; ++j) {

cin >> ch;

h[i][j] = h[i][j - 1] * 131 + ch;

}

for(int i = 1; i <= a; ++i)

for(int j = 1; j <= b; ++j) {

h[i][j] = h[i][j] + h[i - 1][j] * 233;

}

cout << (mp[h[a][b]] ? 1 : 0) << endl;

}

}



#include <bits/stdc++.h>

#define int long long

#define rep(i, a, b) for(int i = a; i <= b; ++i)



using namespace std;

const int N = 2e6 + 10;

const int mod1 = 1e9 + 7, mod2 = 1e9 + 9;



struct pii {

int p1, p2;

pii() {}

pii(int p1_, int p2_) : p1(p1_), p2(p2_) {}

pii operator + (pii t) {

t.p1 = (this->p1 + t.p1) % mod1;

t.p2 = (this->p2 + t.p2) % mod2;

return t;

}

pii operator - (pii t) {

t.p1 = (this->p1 - t.p1 + mod1) % mod1;

t.p2 = (this->p2 - t.p2 + mod2) % mod2;

return t;

}

pii operator * (pii t) {

t.p1 = (this->p1 * t.p1) % mod1;

t.p2 = (this->p2 * t.p2) % mod2;

return t;

}

bool operator == (pii t) {

if(t.p1 == this->p1 && t.p2 == this->p2) return true;

return false;

}

}p[N], pre[N], suf[N];

signed main() {

int n;//拼凑的长度

string s;

cin >> n >> s;

pii P = {131, 13331};

p[0] = {1, 1};

rep(i, 1, (n << 1)) {

p[i] = p[i - 1] * P;

pii tt = {s[i - 1] - 'a', s[i - 1] - 'a'};

pre[i] = pre[i - 1] * P + tt;

}

for(int i = (n << 1); i >= 0; --i) {

pii tt = {s[i - 1] - 'a', s[i - 1] - 'a'};

suf[i] = suf[i + 1] * P + tt;

}

rep(i, 0, n) {

pii sum1 = (pre[i] * p[n - i]) + (pre[(n << 1)] - pre[i + n] * p[n - i]);

pii sum2 = suf[i + 1] - suf[i + n + 1] * p[n];

if(sum1 == sum2) {

cout << s.substr(0, i) << s.substr(i + n) << "\n" << i << "\n";

return 0;

}

}

cout << "-1\n";

return 0;

}

 

 

6.5 Kmp

int n, m;

char s[N], p[N];

int ne[N];

signed main() {

cin >> s + 1 >> p + 1;

n = strlen(s + 1), m = strlen(p + 1);

for(int i = 2, j = 0; i <= m; ++i) {

while(j && p[i] != p[j + 1]) j = ne[j];

if(p[i] == p[j + 1]) ++j;

ne[i] = j; //更新ne值

}

for(int i = 1, j = 0; i <= n; ++i) {

while(j && s[i] != p[j + 1]) j = ne[j];

if(s[i] == p[j + 1]) ++j;

if(j == m) {

cout << i - m + 1 << "\n"; //第一个字符的位置

j = ne[j];

}

}

}

 

6.6 Manacher

/*

*p[i] 回文半径 p[i] - 1即是回文串长度

*(i - p[i] + 1) / 2 为原回文串长度为p[i] - 1的起点

*/

char s[N], t[N << 1];

int n, p[N << 1];

void manacher() {

int L = 0;

t[++L] = '$', t[++L] = '#';

for(int i = 1; i <= n; ++i) t[++L] = s[i], t[++L] = '#';

int mx = 0, id, ans = -1;

for(int i = 1; i <= L; ++i) {

if(mx >= i) p[i] = min(mx - i, p[2 * id - i]);

else p[i] = 1;

while(t[i - p[i]] == t[i + p[i]]) ++p[i];

if(i + p[i] > mx) mx = i + p[i], id = i;

if(ans < p[i]) ans = p[i];

}

cout << ans - 1;

}

/*

*判断某个区间是否是回文

*/

bool check(int L, int R) {

int mid = L + R + 1;

return 2 * p[mid] - 1 >= 2 * (R - L);

}

 

6.7 后缀数组

/*

*sa[i]:排名为i的后缀开始

*hel[i]:sa[i]与sa[i - 1] 的最长公共前缀lcp

*某个字符串的本质不同的子串是n * (n + 1) / 2 - sum(hel[i])

*/

int n, m; //m 值域

char s[N];

int sa[N], hel[N];

int h1[N], h2[N], rk[N], c[N];

void SA() {

int *x = h1, *y = h2;

for(int i = 1; i <= n; ++i) c[x[i] = s[i]]++;

for(int i = 2; i <= m; ++i) c[i] += c[i - 1];

for(int i = n; i; --i) sa[c[x[i]]--] = i; //第一关键字排序

for(int k = 1; k <= n; k <<= 1) {

int num = 0;

for(int i = n - k + 1; i <= n; ++i) y[++num] = i;

for(int i = 1; i <= n; ++i) {

if(sa[i] > k) y[++num] = sa[i] - k;

}

for(int i = 1; i <= m; ++i) c[i] = 0;

for(int i = 1; i <= n; ++i) c[x[i]]++;

for(int i = 2; i <= m; ++i) c[i] += c[i - 1];

for(int i = n; i; --i) sa[c[x[y[i]]]--] = y[i], y[i] = 0;

swap(x, y);

x[sa[1]] = 1, num = 1;

for(int i = 2; i <= n; ++i)

x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) == 1 ? num : ++num;

if(num == n) break;

m = num;

}

for(int i = 1; i <= n; i++) rk[sa[i]] = i;

for(int i = 1, k = 0; i <= n; i++) {

if(rk[i] == 1) continue;

if(k) k--;

int j = sa[rk[i] - 1];

while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;

hel[rk[i]] = k;

}

}

/*

*2个串求公共最长子串

*len1为第一个串的长度

*/

for(int i = 1; i <= n; ++i) {

if((sa[i] <= len1) != (sa[i - 1] <= len1)) {

if(hel[i] > ans) ans = hel[i];

}

}

 

标签:cnt,return,int,线段,mid,++,void
From: https://www.cnblogs.com/jkc9/p/17359921.html

相关文章

  • 线段树的动态开点模板
    学习自数据结构学习笔记(5)动态开点线段树动态开点线段树感谢大佬们博客的帮助//AConemoretimes#include<bits/stdc++.h>usingnamespacestd;#definefifirst#definesesecond#definepbpush_back#defineendl'\n'#defineall(x)(x).begin(),(x).end()......
  • CF960F Pathwalks | 线段树优化DP
    题目设\(dp[x,w]\)为以结点\(x\)为结尾,且最后一条边边权为\(w\)的最长路径长度。考虑根据顺序加边,对于边\((u,v)\),更新\[dp[v,w]=\max_{w'<w}\{dp[u,w']\}+1\]对于每个节点,建一棵线段树,维护\(dp[x]\),这样每次更新\(dp[v,w]\)就相当于在\(dp[u]\)所对应的线段树中查询\([......
  • 线段树
    1#include<iostream>2#include<string>3#definelllonglong4constintN=1e5+5;56usingnamespacestd;78lltree[N<<2];//线段树,可以是对应的结构体9lllz[N<<2];//延迟标记,也可以是结构体1011//lz标记下传12voidpush_down(intid......
  • HDU - 5649 线段树+区间二分答案 (好题)
    DZYhasasequencea[1…n].Itisapermutationofintegers1∼n.Nowhewantstoperformtwotypesofoperations:0lr:Sorta[l…r]inincreasingorder.1lr:Sorta[l…r]indecreasingorder.Afterdoingalltheoperations,hewilltellyouapositionk,andask......
  • [Week 18] 每日一题(C++,动态规划,线段树,数学)
    目录[Daimayuan]T1最长公共子序列(C++,DP,二分)输入格式输出格式数据范围输入样例输出样例解题思路[Daimayuan]T2喵喵序列(C++,序偶)题目描述输入格式输出格式样例输入样例输出样例说明数据范围双倍经验解题思路:[Daimayuan]T3漂亮数(C++,字符串)输入描述输出描述输入样例输出样例解题......
  • codeforces 573B B. Bear and Blocks(线段树+dp)
    题目链接:codeforces573B题目大意:给出n个连续塔,每个塔有高度hi,每次取走最外层的块,问需要多少次操作能够拿光所有的块。题目分析:首先我们可以知道第一次操作时,对于每个塔的变化满足如下的公式:hi=min(hi−1,hi−1,hi+1)每次操作都满足如下的递推式,我们递推一下得到第k次操作第i......
  • CF1797E 线段树 + 倍增 题解
    Preface有趣的一道ds,赛后不看题解做出来了。Solution首先有一个性质:\(\varphi(x)\)经过\(\mathcal{O}(\logx)\)次迭代后变为\(1\)。证明:若\(x\)为奇数,\(\varphi(x)=x\sum_{i=1}^{k}\frac{p_i-1}{p_i}\),\(p_i\)为奇数,所以\(p_i-1\)为偶数,我们就能得到\(\varphi(x)......
  • ARC159F Good Division【性质,DP,线段树】
    定义一个序列是好的当且仅当其可以通过每次删去一对相邻的不同的数把序列删空。给定一个长度为\(2n\)的序列\(a\),求有多少种划分方式使得每一段都是好的。答案对\(998244353\)取模。\(n\leq5\times10^5\),时限\(\text{5.0s}\)。先考虑什么样的数列是合法的,显然必要条......
  • Codeforces Round 850 (Div. 2, based on VK Cup 2022 - Final Round) E. Monsters (h
    传送门详细题解传送门  抄的ygg代码,向在这里说一下刚开始没看懂的部分。  求答案的时候是把所有的当前为止的所有数值加起来减去一个从1开始并且公差为1的等差数列的前size项和。其中size是当前最多能用到哪个位置,满足前size项能构成1,2,3,....,sz这样的形式。  假设我们......
  • xor (牛客多校) (线性基+ 线段树)
      思路:问xor起来有没有某个值,想到线性基然后发现问L-R区间的集合都要表示x,利用线性基的交集解决在利用线段树解决区间问题 #include<iostream>usingnamespacestd;typedefunsignedintui;constintmaxn=50005;structL_B{//线性基结构体ui......