基础算法

离散化

  1. int lisa[maxn];int tail = 0;
  2. void init(){
  3. sort(lisa+1,lisa+tail+1);
  4. tail = unique(lisa+1,lisa+tail+1)-lisa-1;
  5. }
  6. int ind(int x){
  7. return lower_bound(lisa+1,lisa+tail+1,x) - lisa;
  8. }

初始化模板

#include <bits/stdc++.h>
#define ios ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define debug_in  freopen("in.txt","r",stdin)
#define deubg_out freopen("out.txt","w",stdout);
#define pb push_back
#define all(x) x.begin(),x.end()
#define fs first
#define sc second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int maxn = 1e6+10;
const int maxM = 1e6+10;
const int inf = 0x3f3f3f3f;
const ll inf2 = 0x3f3f3f3f3f3f3f3f;

template<class T>void read(T &x){
    T s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    x = s*w;
}
template<class H, class... T> void read(H& h, T&... t) {
    read(h);
    read(t...);
}
template <typename ... T>
void DummyWrapper(T... t){}

template <class T>
T unpacker(const T& t){
    cout<<' '<<t;
    return t;
}
template <typename T, typename... Args>
void pt(const T& t, const Args& ... data){
    cout << t;
    DummyWrapper(unpacker(data)...);
    cout << '\n';
}


//--------------------------------------------

int main(){
    debug_in;

    return 0;
}

__int128


void read(__int128 &x) {
    x = 0;
    char ch;
    int flag = 1;
    while (ch = getchar()) {
        if (ch == '-') flag = -1;
        if (ch >= '0' && ch <= '9') break;
    }
    x = ch-'0';
    while ((ch = getchar()) >= '0' && ch <= '9') {
        x = x*10 + ch-'0';
    }
    x *= flag;
}


void out(__int128 x) {
    if (x < 0) {
        x = -x;
        putchar('-');
    }
    if (x >= 10) out(x / 10);
    putchar(x % 10 +'0');
}
f(x) = a*x*x + b *x + c 二元一次方程顶点公式:(-b/2a,(4ac-b2)/4a)

树相关

Cayley公式:对于n个不同的节点,能够组成的无根树(原来是无向连通图或者是有标志节点的树)的种数是n^(n-1)种
有根树:n^(n-1)

调和级数相关

求n/1 + n/2 + n/3 + … + n/k

ll solve(ll n,ll k){
    ll m=sqrt(n);
    ll ans=0;
    for(ll i=1;i<=k&&i<=m;i++){
        ans=(ans+n/i);
    }
    if(k<=m){
        return ans;
    }
    else{
        for(ll i=n/min(n,k);i<=m;i++){
            ans =(ans+ (n/i - n/(i+1)) * i);
        }
        if(k<n){
            ans-=(n/(n/k)-k)*(n/k);
        }
        if( n / m == m)
            ans -= m;
    }
    return ans;
}

求卡特兰数 (递推方式)

int N;
ll f[maxn];
f[0] = 1;f[1] = 1;
for(int i = 2;i<=N;i++){
    for(int j = 0;j<=i-1;j++){
        f[i] += f[j] * f[i-1-j];
    }
}

求卡特兰数 (公式方式 C(2n,n)/(n+1))

ll f[maxn];
f[0]=f[1]=1;
for(int i=2;i<=n;i++) f[i]+=f[i-1]*(4*i-2)/(i+1);

分数计算

struct node
{
public:
    ll son,mu;
    ll gcd(ll a,ll b){
        return !b? a : gcd(b,a%b);
    }
    void init(){
        ll g = gcd(son,mu);
        son/=g,mu/=g;
    }
    bool operator < (const node &o){
        return son * o.mu < o.son * mu;
    }
    friend node operator + (node &a,node &b){
        node ans;
        if(a.son == 0) return b;
        if(b.son == 0) return a;
        ans.mu = a.mu * b.mu;
        ans.son = a.son * b.mu + a.mu * b.son;
        ans.init();
        return ans;
    }
    friend node operator /(node a,ll x){
        node ans = a;
        ans.mu *= x;
        ans.init();
        return ans;
    }
};

组合数大礼包

struct AC
{
    static const int maxn = 1e6+10;
    ll f[maxn];
    void init(int mod){ //预处理阶乘
        f[0] = 1;
        for(int i = 1;i<maxn;i++) f[i] = f[i-1]*i%mod;
    }
    ll ksm(ll a,ll b,ll mod){
        ll ans = 1;
        while(b){
            if(b&1) ans = ans*a%mod;
            a = a*a%mod;
            b>>=1;
        }
        return ans;
    }
    ll C_mod(ll n,ll m,ll mod){//组合数%mod
        return f[n] * ksm(f[m] * f[n-m]%mod,mod-2,mod)%mod;
    }
    ll lucas_mod(ll n,ll m,ll mod){
        return m? C_mod(n%mod,m%mod,mod) * lucas_mod(n/mod,m/mod,mod)%mod : 1;
    }
    ll A(ll n, ll r)//排列数
    {    
        ll sum = 1;
        for (int i = 0; i < r; i++)
            sum *= n-i;
        return sum;
    }
    ll C(ll n,ll K){ //组合数
        if(K>n) return 0;
        if(K > n-K) K = n-K;
        ll m = 1,s = 1;
        for(int i = 0;i<K;i++){
            m = m*(n-i);
            s = s*(i+1);
        }
        return m/s;
    }
}ac;

计算几何

struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x;y = _y;
    }
    Point operator -(const Point &b)const{
        return Point(x - b.x,y - b.y);
    }
    double operator *(const Point &b)const{
        return x*b.x + y*b.y;
    }
};

//两点之间的距离
double dist(Point a,Point b){
    return sqrt((a-b)*(a-b));
}
//两个圆相交的面积
double Area_of_overlap(Point c1,double r1,Point c2,double r2)
{
    double d = dist(c1,c2);
    if(r1 + r2 < d + eps)return 0;
    if(d < fabs(r1 - r2) + eps)
    {
        double r = min(r1,r2);
        return PI*r*r;
    }
    double x = (d*d + r1*r1 - r2*r2)/(2*d);
    double t1 = acos(x / r1);
    double t2 = acos((d - x)/r2);
    return r1*r1*t1 + r2*r2*t2 - d*r1*sin(t1);
}

矩阵快速幂

struct Mat
{
    int mat[33][33];
    Mat(){
        memset(mat,0,sizeof mat);
    }
    Mat operator *(const Mat &b)const {
        Mat res;
        memset(&res,0,sizeof res);
        for(int i = 0;i<=N;i++){
            for(int j = 0;j<=N;j++){
                for(int k = 0;k<=N;k++){
                    res.mat[i][j] += mat[i][k] * b.mat[k][j] % mod; res.mat[i][j]%=mod;
                }
            }
        }
        return res;
    }

};
Mat ksm(Mat M,int b){
    Mat res;
    memset(&res,0,sizeof res);
    for(int i = 0;i<=N;i++) res.mat[i][i] = 1;
    while(b){
        if(b&1) res = res * M;
        M = M*M;
        b>>=1;
    }
    return res;
}

高精度

struct bign{ 
    int d[50], len; 

    void clean() { while(len > 1 && !d[len-1]) len--; } 

    bign()          { memset(d, 0, sizeof(d)); len = 1; } 
    bign(int num)   { *this = num; }  
    bign(char* num) { *this = num; } 
    bign operator = (const char* num){ 
        memset(d, 0, sizeof(d)); len = strlen(num); 
        for(int i = 0; i < len; i++) d[i] = num[len-1-i] - '0'; 
        clean(); 
        return *this; 
    } 
    bign operator = (int num){ 
        char s[20]; sprintf(s, "%d", num); 
        *this = s; 
        return *this; 
    } 

    bign operator + (const bign& b){ 
        bign c = *this; int i; 
        for (i = 0; i < b.len; i++){ 
            c.d[i] += b.d[i]; 
            if (c.d[i] > 9) c.d[i]%=10, c.d[i+1]++; 
        } 
        while (c.d[i] > 9) c.d[i++]%=10, c.d[i]++; 
        c.len = max(len, b.len); 
        if (c.d[i] && c.len <= i) c.len = i+1; 
        return c; 
    } 
    bign operator - (const bign& b){ 
        bign c = *this; int i; 
        for (i = 0; i < b.len; i++){ 
            c.d[i] -= b.d[i]; 
            if (c.d[i] < 0) c.d[i]+=10, c.d[i+1]--; 
        } 
        while (c.d[i] < 0) c.d[i++]+=10, c.d[i]--; 
        c.clean(); 
        return c; 
    } 
    bign operator * (const bign& b)const{ 
        int i, j; bign c; c.len = len + b.len;  
        for(j = 0; j < b.len; j++) for(i = 0; i < len; i++)  
            c.d[i+j] += d[i] * b.d[j]; 
        for(i = 0; i < c.len-1; i++) 
            c.d[i+1] += c.d[i]/10, c.d[i] %= 10; 
        c.clean(); 
        return c; 
    } 
    bign operator / (const bign& b){ 
        int i, j; 
        bign c = *this, a = 0; 
        for (i = len - 1; i >= 0; i--) 
        { 
            a = a*10 + d[i]; 
            for (j = 0; j < 10; j++) if (a < b*(j+1)) break; 
            c.d[i] = j; 
            a = a - b*j; 
        } 
        c.clean(); 
        return c; 
    } 
    bign operator % (const bign& b){ 
        int i, j; 
        bign a = 0; 
        for (i = len - 1; i >= 0; i--) 
        { 
            a = a*10 + d[i]; 
            for (j = 0; j < 10; j++) if (a < b*(j+1)) break; 
            a = a - b*j; 
        } 
        return a; 
    } 
    bign operator += (const bign& b){ 
        *this = *this + b; 
        return *this; 
    } 

    bool operator <(const bign& b) const{ 
        if(len != b.len) return len < b.len; 
        for(int i = len-1; i >= 0; i--) 
            if(d[i] != b.d[i]) return d[i] < b.d[i]; 
        return false; 
    } 
    bool operator >(const bign& b) const{return b < *this;} 
    bool operator<=(const bign& b) const{return !(b < *this);} 
    bool operator>=(const bign& b) const{return !(*this < b);} 
    bool operator!=(const bign& b) const{return b < *this || *this < b;} 
    bool operator==(const bign& b) const{return !(b < *this) && !(b > *this);} 

    string str() const{ 
        char s[maxn]={}; 
        for(int i = 0; i < len; i++) s[len-1-i] = d[i]+'0'; 
        return s; 
    } 
}; 
istream& operator >> (istream& in, bign& x) 
{ 
    string s; 
    in >> s; 
    x = s.c_str(); 
    return in; 
} 

ostream& operator << (ostream& out, const bign& x) 
{ 
    out << x.str(); 
    return out; 
}

熟链剖分

#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <map>
#include <stack>
#include <vector>
#define ios ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define debug  freopen("in.txt","r",stdin),freopen("out.txt","w",stdout);
#define pb push_back
#define all(x) x.begin(),x.end()
#define fs first
#define sc second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
const int maxn = 1e5+10;
const int maxM = 1e6+10;
const int inf = 0x3f3f3f3f;
const ll inf2 = 0x3f3f3f3f3f3f3f3f;


int N,M,R,mod;
int w[maxn];
vector<int> adj[maxn];
struct segtree{
    struct node{
        int l,r;
        ll sum,lazy;
    }tr[maxn*4];
    void pushup(node &cur,node &L,node &R){
        cur.sum = L.sum + R.sum;
        cur.sum %= mod;
    }
    void pushdown(node &cur,node &L,node &R){
        if(cur.lazy == 0) return ;
        L.lazy += cur.lazy; L.lazy %= mod;
        R.lazy += cur.lazy; R.lazy %= mod;
        L.sum += (ll)(L.r - L.l + 1) * cur.lazy; L.sum %= mod;
        R.sum += (ll)(R.r - R.l + 1) * cur.lazy; R.sum %= mod;
        cur.lazy = 0;
    }
    void build(int l,int r,int u = 1){
        tr[u] = {l,r};
        if(l != r){
            int mid = (l+r)>>1;
            build(l,mid,u*2);
            build(mid+1,r,u*2+1);
        }
    }
    void modify(int l,int r,int v,int u = 1){
        if(l <= tr[u].l && tr[u].r <= r){
            tr[u].lazy += v; tr[u].lazy %= mod;
            tr[u].sum += (tr[u].r - tr[u].l +1) * v; tr[u].sum %= mod;
        }else{
            pushdown(tr[u],tr[u*2],tr[u*2+1]);
            int mid  = (tr[u].l + tr[u].r) >>1;
            if(l<=mid) modify(l,r,v,u*2);
            if(r>mid) modify(l,r,v,u*2+1);
            pushup(tr[u],tr[u*2],tr[u*2+1]);
        }
    }
    ll query(int l,int r,int u = 1){
        if(l <= tr[u].l && tr[u].r <= r){
            return tr[u].sum;
        }else{
            pushdown(tr[u],tr[u*2],tr[u*2+1]);
            int mid = (tr[u].l + tr[u].r)>>1;
            ll sum = 0;
            if(l<=mid) sum += query(l,r,u*2), sum%=mod;
            if(r > mid) sum += query(l,r,u*2+1),sum%=mod;
            return sum;
        }
    }

}tree;

struct treepou{
    int tt = 0;
    int tim[maxn],id[maxn],sz[maxn],hson[maxn],fa[maxn],top[maxn],dep[maxn];
    void init(){
        dep[1] = 1;
    }
    void dfs1(int u){ //子树大小,每个节点的重孩子,每个孩子的父亲,节点的深度
        sz[u] = 1;
        for(auto v:adj[u]){
            if(v == fa[u]) continue;
            dep[v] = dep[u] + 1;
            fa[v] = u;
            dfs1(v);
            sz[u] += sz[v];
            if(sz[v] > sz[hson[u]]){
                hson[u] = v;
            }
        }
    }
    void dfs2(int u,int t){//弄出dfs序,每个节点的链头
        tim[u] = ++tt;
        id[tt] = u;
        top[u] = t;

        if(!hson[u]) return ;
        dfs2(hson[u],t);
        for(auto v:adj[u]){
            if(v == fa[u] || v == hson[u]) continue;
            dfs2(v,v);
        }
    }
    void modify_tree(int x,int v){ //给根节点为x的子树的所有节点加v
        tree.modify(tim[x],tim[x]+sz[x]-1,v);
    }
    ll query_tree(int x){ //计算根节点为x的子树的点权和
        return tree.query(tim[x],tim[x]+sz[x]-1);
    }
    void modify_line(int x,int y,int v){//修改x到y的链上的点权,统一加1
        while(top[x] != top[y]){
            if(dep[top[x]] < dep[top[y]]) swap(x,y);
            tree.modify(tim[top[x]],tim[x],v);
            x = fa[top[x]];
        }
        if(dep[x] > dep[y]) swap(x,y);
        tree.modify(tim[x],tim[y],v);
    }
    ll query_line(int x,int y){ //查询x到y的链上的点权和
        ll ans = 0;
        while(top[x] != top[y]){
            if(dep[top[x]] < dep[top[y]]) swap(x,y);
            ans += tree.query(tim[top[x]],tim[x]); ans %= mod;
            x = fa[top[x]];
        }
        if(dep[x] > dep[y]) swap(x,y);
        ans += tree.query(tim[x],tim[y]); ans%=mod;
        return ans;
    }

}T;
void solve(){
    tree.build(1,N);
    T.init();
    T.dfs1(R);
    T.dfs2(R,R);
    for(int i =1;i<=N;i++) tree.modify(T.tim[i],T.tim[i],w[i]%mod);

    while(M--){
        int op,x,y,z;
        cin>>op;
        if(op == 1){
            cin>>x>>y>>z;
            T.modify_line(x,y,z);
        }else if(op == 2){
            cin>>x>>y;
            cout<<T.query_line(x,y)<<'\n';
        }else if(op == 3){
            cin>>x>>z;
            T.modify_tree(x,z);
        }else{
            cin>>x;
            cout<<T.query_tree(x)<<'\n';
        }
    }
}
int main(){
//    debug;
    ios;

    cin>>N>>M>>R>>mod;
    for(int i =1;i<=N;i++) cin>>w[i];
    for(int i = 1;i<=N-1;i++){
        int x,y;cin>>x>>y;
        adj[x].pb(y);
        adj[y].pb(x);
    }
    solve();
    return 0;
}

带权并查集

ll fa[maxn],dis[maxn];
ll find(ll x){
    if(fa[x] != x){
        ll f = fa[x];
        fa[x] = find(fa[x]);
        dis[x] += dis[f];
    }
    return fa[x];
}
bool join(ll x,ll y,ll v){
    ll fx = find(x),fy = find(y);
    if(fx != fy){
        fa[fy] = fx;
        dis[fy] = dis[x] + v - dis[y];
    }else if(dis[y] - dis[x] != v){ //权值冲突
        return false;
    }
    return true;
}

强连通分量SCC

struct SCC
{
    static const int maxn = 1e5+10;
    int dfn[maxn],low[maxn],scc_id[maxn],scc_sz[maxn],scc,tim;
    int sk[maxn],top;bool in_sk[maxn];

    void do_scc(){
        for(int i = 1;i<=N;i++){ // N是节点个数
            if(!dfn[i]){
                tarjan(i);
            }
        }
    }
    void tarjan(int u){
        dfn[u] = low[u] = ++tim;
        sk[++top] = u,in_sk[u] = true;
        for(int i = h[u];i;i = ne[i]){ // 遍历原来的图
            int v = e[i];
            if(!dfn[v]){
                tarjan(v);
                low[u] = min(low[u],low[v]);
            }else if(in_sk[v]){
                low[u] = min(low[u],dfn[v]);
            }
        }
        if(low[u] == dfn[u]){
            ++scc;
            int v;
            do{
                v = sk[top--],in_sk[v] = false;
                scc_sz[scc]++;
                scc_id[v] = scc;
            }while(v != u);
        }
    }
    void init_dag(){ //构建DAG图
        for(int u = 1;u<=N;u++){
            for(int i = h[u];i;i = ne[i]){
                int v = e[i],ww = w[i];
                int idu = scc_id[u],idv = scc_id[v];
                if(idu != idv){
                    dag[idu].pb({idv,ww});
                    in[idv]++;
                }
            }
        }
    }
}tar;

二分图最大匹配

#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <map>
#include <stack>
#include <vector>
#define ios ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define debug  freopen("in.txt","r",stdin),freopen("out.txt","w",stdout);
#define pb push_back
#define all(x) x.begin(),x.end()
#define fs first
#define sc second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
const int maxn = 1e6+10;
const int maxM = 1e6+10;
const int inf = 0x3f3f3f3f;
const ll inf2 = 0x3f3f3f3f3f3f3f3f;
inline void read(int &x){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    x = s*w;
}

int N,M,E;
vector<int> adj[maxn];
int match[maxn];
bool st[555];
bool find(int u){
    for(auto v:adj[u]){
        if(!st[v]){
            st[v] = true;
            if(match[v] == 0 || find(match[v])){
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}
void solve(){
    int ans = 0;
    for(int i =1;i<=N;i++){
        memset(st,0,M+10);
        if(find(i)) ans++;
    }
    cout<<ans<<'\n';
}
int main(){
//    debug;
    ios;

    cin>>N>>M>>E;
    for(int i =1;i<=E;i++){
        int x,y;cin>>x>>y;
        adj[x].pb(y);
    }
    solve();

    return 0;
}

平衡树

struct Treap{
    int root,idx;
    struct node{
        int l,r;
        int key,val;
        int cnt,size;
    }tr[100010];
    void pushup(int p){
        tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt;
    }
    int get_node(int key){
        tr[++idx].key = key;
        tr[idx].val = rand();
        tr[idx].cnt = tr[idx].size = 1;
        return idx;
    }
    void zig(int &p){
        int q = tr[p].l;
        tr[p].l = tr[q].r,tr[q].r = p,p = q;
        pushup(tr[p].r),pushup(p);
    }
    void zag(int &p){
        int q = tr[p].r;
        tr[p].r = tr[q].l,tr[q].l = p,p = q;
        pushup(tr[p].l),pushup(p);
    }
    void build(){
        get_node(-inf);get_node(inf);
        root = 1,tr[1].r = 2;
        pushup(root);
        if(tr[1].val < tr[2].val) zag(root);
    }
    void insert(int &p,int key){
        if(!p) p = get_node(key);
        else if(tr[p].key == key) tr[p].cnt++;
        else if(key < tr[p].key){
            insert(tr[p].l,key);
            if(tr[tr[p].l].val > tr[p].val) zig(p);
        }else{
            insert(tr[p].r,key);
            if(tr[tr[p].r].val > tr[p].val) zag(p);
        }
        pushup(p);
    }
    void remove(int &p,int key){
        if(!p) return ;
        if(tr[p].key == key){
            if(tr[p].cnt > 1) tr[p].cnt--;
            else if(tr[p].l || tr[p].r){
                if(!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val){
                    zig(p);
                    remove(tr[p].r,key);
                }else{
                    zag(p);
                    remove(tr[p].l,key);
                }
            }else p = 0;
        }else if(key < tr[p].key) remove(tr[p].l,key);
        else remove(tr[p].r,key);
        pushup(p);
    }
    int get_rank_by_key(int p,int key){
        if(!p) return 0;
        if(key < tr[p].key) return get_rank_by_key(tr[p].l,key);
        else if(key == tr[p].key) return tr[tr[p].l].size + 1;
        else return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r,key);
    }
    int get_key_by_rank(int p,int rank){
        if(!p) return inf;
        if(rank <= tr[tr[p].l].size) return get_key_by_rank(tr[p].l,rank);
        else if(rank <= tr[tr[p].l].size + tr[p].cnt) return tr[p].key;
        else return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
    }
    int get_prev(int p,int key){
        if(!p) return -inf;
        if(key <= tr[p].key) return get_prev(tr[p].l,key);
        else return max(tr[p].key,get_prev(tr[p].r,key));   
    }
    int get_next(int p,int key){
        if(!p) return inf;
        if(key >= tr[p].key) return get_next(tr[p].r,key);
        else return min(tr[p].key,get_next(tr[p].l,key));
    }
}trp;

使用方法

trp.build()
操作复杂度均为log
trp.insert(trp.root,v); //插入v
trp.remove(trp.root,v); //删除v
printf("%d\n",trp.get_rank_by_key(trp.root,v)-1);//因为最开始放了一个-inf,和inf,这里排名会比实际的排名-1
printf("%d\n",trp.get_key_by_rank(trp.root,v+1));//查询的时候,要查询比实际排名+1
printf("%d\n",trp.get_prev(trp.root,v));//获取前驱节点key
printf("%d\n",trp.get_next(trp.root,v));//获取后继节点key

线段树

#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <map>
#include <stack>
#include <vector>
#define ios ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define debug  freopen("in.txt","r",stdin),freopen("out.txt","w",stdout);
#define pb push_back
#define all(x) x.begin(),x.end()
#define fs first
#define sc second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
const int maxn = 1e5+10;
const int maxM = 1e6+10;
const int inf = 0x3f3f3f3f;
const ll inf2 = 0x3f3f3f3f3f3f3f3f;

int N,M;
int a[maxn];

struct segtree{
    struct node{
        int l,r;
        ll sum,lazy;
    }tr[maxn*4];
    void pushup(node &cur,node &L,node &R){
        cur.sum = L.sum + R.sum;
    }
    void pushdown(node &cur,node &L,node &R){
        if(cur.lazy == 0) return ;
        L.lazy += cur.lazy;
        R.lazy += cur.lazy;
        L.sum += (ll)(L.r - L.l + 1) * cur.lazy;
        R.sum += (ll)(R.r - R.l + 1) * cur.lazy;
        cur.lazy = 0;
    }
    void build(int l,int r,int u = 1){
        tr[u] = {l,r};
        if(l != r){
            int mid = (l+r)>>1;
            build(l,mid,u*2);
            build(mid+1,r,u*2+1);
        }
    }
    void modify(int l,int r,int v,int u = 1){
        if(l <= tr[u].l && tr[u].r <= r){
            tr[u].lazy += v;
            tr[u].sum += (tr[u].r - tr[u].l +1) * v;
        }else{
            pushdown(tr[u],tr[u*2],tr[u*2+1]);
            int mid  = (tr[u].l + tr[u].r) >>1;
            if(l<=mid) modify(l,r,v,u*2);
            if(r>mid) modify(l,r,v,u*2+1);
            pushup(tr[u],tr[u*2],tr[u*2+1]);
        }
    }
    ll query(int l,int r,int u = 1){
        if(l <= tr[u].l && tr[u].r <= r){
            return tr[u].sum;
        }else{
            pushdown(tr[u],tr[u*2],tr[u*2+1]);
            int mid = (tr[u].l + tr[u].r)>>1;
            ll sum = 0;
            if(l<=mid) sum += query(l,r,u*2);
            if(r > mid) sum += query(l,r,u*2+1);
            return sum;
        }
    }

}tree;
int main(){
//    debug;
    ios;

    cin>>N>>M;
    tree.build(1,N);
    for(int i =1;i<=N;i++) {
        int cur;cin>>cur;
        tree.modify(i,i,cur); //在区间[i,i]的元素加一个cur,相当于单点修改
    }
    for(int i =1 ;i<=M;i++){
        int op,x,y,k;cin>>op>>x>>y;
        if(op == 1){
            cin>>k;
            tree.modify(x,y,k);//在区间[x,y] 加k
        }else{
            cout<<tree.query(x,y)<<'\n'; // 查询区间[x,y]的和
        }
    }


    return 0;
}

多重背包

对于物品而言最多能够选择从s[i]个,同样也可不选
dp[j] : 体积为j能获得的最大价值

int N,V;
int w[maxn],v[maxn],s[maxn]; // 权值,价值,数量
ll dp[maxn];
int main(){
    cin>>N>>V;
    for(int i = 1;i<=N;i++) scanf("%d %d %d",&w[i],&v[i],&s[i]);
    for(int i = 1;i<=N;i++){
        for(int j = V;j>=w[i];j--){
            for(int k = 1;k<=s[i] && k*w[i]<=j;k++){
                dp[j] = max(dp[j],dp[j - k*w[i]] + k*v[i]);
            }
        }
    }
}

多重背包,二进制拆分优化

将多重背包转换成01背包

int N,V;
int w[maxn],v[maxn],tail;//tail是二进制拆分后的物品数
ll dp[maxn];
int main(){
    cin>>N>>V;
    for(int i = 1;i<=N;i++){
        int ww,vv,ss;scanf("%d %d %d",&ww,&vv,&ss);
        for(int k = 1; k <= ss; k*=2){
            ss -=k;
            w[++tail] = k * ww;
            v[tail] = k * vv;
        }
        if(ss) w[++tail] = ss*ww,v[tail] = ss*vv;
    }
    for(int i = 1;i<=tail;i++){
        for(int j =  V;j>=w[i];j--){
            dp[j] = max(dp[j],dp[j-w[i]] + v[i]);
        }
    }

分组背包

每一组物品中只能选择其中的一个物品
dp[j] : 体积为j能获得的最大价值

int T,N,V;
int G[110][1010],cnt[110];
int w[maxn],v[maxn];
ll dp[maxn];
cin>>V>>N;
    for(int i = 1;i<=N;i++){
        int t;
        scanf("%d %d %d",&w[i],&v[i],&t);
        T = max(T,t);//记录最大组数
        G[t][++cnt[t]] = i; //记录每组每个物品的编号
    }
    for(int i = 1;i<=T;i++){ //组数
        for(int j = V;j>=0;j--){ //体积 : 因为是倒着循环的,故只会从一组中选一次
            for(int k = 1;k<=cnt[T];k++){//物品
                if(j - w[G[i][k]]>=0) dp[j] = max(dp[j],dp[j - w[G[i][k]]] + v[G[i][k]]);
            }
        }
    }

ST表

静态求区间最大值,基于倍增的思想

int N,M;
int arr[maxn],Log2[maxn];//原始数组,log2(x)数组
int f[maxn][20];//F[i][j]: arr[i~i+2^j-1]的最大值

void ST_init(){ //初始化所有长度为2^x的区间最大值
    for(int i = 1;i<=N;i++) Log2[i] = log(i)/log(2); //初始化log求值,之后O(1)取值
    for(int i = 1;i<=N;i++) f[i][0] = arr[i];
    int len = log(N)/log(2) +1;
    for(int j = 1;j<len;j++){
        for(int i = 1;i<=N-(1<<j)+1;i++){
            f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
}
int query(int l,int r){ //查询arr[l~r]区间的最值
    int k = Log2[r-l+1];
    return max(f[l][k],f[r-(1<<k)+1][k]);
}

奇奇怪挂的数据结构

马拉车算法

string s;
int ans[maxn],str[maxn],lef[maxn];
//ans:每一点的回文半径,str:插#字符后的字符串 lef:以某个为左边界的最长回文子串长度
int build(const string &s){
    int n = s.length(), m = (n + 1)*2, ret = 0;
    str[0] = '$'; str[m] = '@'; str[1] = '#'; ans[1] = 1;
    for (int i = 1; i <= n; i++)
        str[i*2] = s[i - 1], str[i*2+1] = '#';
    ans[1] = 1;
    for (int r = 0, p = 0, i = 2; i < m; ++i){
        if (r > i) ans[i] = min(r - i, ans[p * 2 - i]);
        else ans[i] = 1;
        while(str[i - ans[i]] == str[i + ans[i]]) ++ans[i];
        if (i + ans[i] > r) r = i + ans[i], p = i;
        ret = max(ret, ans[i] - 1);
    }
        // 计算维护以每个位置为起点的最长回文串
        for (int i = 0; i <= m; i++) lef[i] = 0;
        for (int i = 2; i < m; i++) if (lef[i - ans[i] + 1] < i + 1) lef[i - ans[i] + 1] = i + 1;
        for (int i = 1; i <= m; i++) if (lef[i] < lef[i - 1]) lef[i] = lef[i - 1];
    return ret;//最长回文串的长度
}
 int mid(int x, bool odd){
     //求以x为中心的最长回文子串长度,若是求偶回文串,这里中心认为是中间靠左
        if (odd) return ans[(x + 1) << 1] - 1;
        return ans[(x + 1) << 1 | 1] - 1;
    }
int left(int x){
    //求以x为左端点的最长回文串的长度
    return lef[(x + 1) << 1] - ((x+1) << 1);
}

树状数组

二维树状数组 [单点修改、区间查询]

int N,M;//N*M的矩阵
int tr[1111][1111];
int lowbit(int x){
    return x&-x;
}
void add(int x,int y,int v){
    for(int i = x;i<=N;i += lowbit(i)){
        for(int j = y;j<=M;j += lowbit(j)){
            tr[i][j] += v;
        }
    }
}
ll pre_sum(int x,int y){ //查询(1,1)到(x,y)矩阵和
    ll sum = 0;
    for(int i = x;i>=1;i -= lowbit(i)){
        for(int j = y;j>=1;j -= lowbit(j)){
            sum += tr[i][j];
        }
    }
    return sum;
}
ll query(int x1,int y1,int x2,int y2){ //查询(x1,y2)到(x2,y2)的矩阵和
    return pre_sum(x2,y2) - pre_sum(x1-1,y2) - pre_sum(x2,y1-1) + pre_sum(x1-1,y1-1);
}

二维树状数组 [区间修改,单点查询]

int tr[1010][1010];//NxN的矩阵

int lowbit(int x){
    return x&-x;
}
void add(int x,int y,int v){
    for(int i = x;i<=N;i += lowbit(i))
        for(int j = y;j<=N;j += lowbit(j))
            tr[i][j] += v;
}
int query(int x,int y){
    int sum = 0;
    for(int i = x;i>=1;i -= lowbit(i))
        for(int j = y;j>=1;j -= lowbit(j))
            sum += tr[i][j];
    return sum;
}
//给左上角(x1,y1) ,右下角(x2,y2)的矩阵+v
add(x1,y1,+v);
add(x1,y2+1,-v);
add(x2+1,y1,-v);
add(x2+1,y2+1,+v);

求树的中心

基于树形dp
请你在树中找到一个点,使得该点到树中其他结点的最远距离最近,找到的这个点就是树的中心

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll maxn = 1e4+10;
const int inf = 0x3f3f3f3f;

int N;
int h[maxn],e[maxn*2],w[maxn*2],ne[maxn*2],idx = 1;
int up[maxn],down1[maxn],down2[maxn],tag1[maxn];
//某个点,往上走的最远距离,往下走的最长距离,往下走的次长距离,往下走最长距离会经过的儿子结点编号
void add(int a,int b,int c){
    e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}

int dfs_down(int u,int fa = -1){ //求每个结点往下走的最长和次长距离,以及最长经过的儿子编号
    int p1 = 0,p2 = 0;
    for(int i = h[u];i;i = ne[i]){
        int v = e[i],wei = w[i];
        if(v == fa) continue;
        int curp = dfs_down(v,u)+wei;
        if(curp > p1){
            p2 = p1,p1 = curp;
            tag1[u] = v;
        }else if(curp > p2){
            p2 = curp;
        }
    }
    down1[u] = p1,down2[u] = p2;//记录一下
    return p1;
}

void dfs_up(int u,int fa = -1){ //父结点更新子结点,找子结点向上的最大距离
    for(int i = h[u];i;i = ne[i]){
        int v = e[i],wei = w[i];
        if(v == fa) continue;
        if(tag1[u] == v){
            up[v] = wei + max(up[u],down2[u]);
        }else{
            up[v] = wei + max(up[u],down1[u]);
        }
        dfs_up(v,u);
    }
}

int main(){
    cin>>N;
    for(int i = 1;i<=N-1;i++){
        int a,b,c;scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    dfs_down(1);
    dfs_up(1);
    int res = inf,H = -1;
    for(int i = 1;i<=N;i++) {
        int d = max(up[i], down1[i]); //向上、向下距离取最大值
        if (d < res) {
            res = d; //中心能移动的最大距离
            H = i;//树的中心
        }
    }

    return 0;
}

倍增法求LCA

int h[maxn],ne[maxn*2],e[maxn*2],idx = 1;
int q[maxn],fa[maxn][21],dep[maxn];
void add(int a,int b){
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void bfs(int root){
    int hh = 0,tt = -1;
    q[++tt] = root;
    dep[0] = 0,dep[root] = 1;

    while(hh<=tt){
        int u = q[hh++];
        for(int i = h[u];i;i = ne[i]){
            int v = e[i];
            if(dep[v]) continue;
            dep[v] = dep[u]+1;
            q[++tt] = v;
            fa[v][0] = u;
            for(int k = 1;k<=20;k++){
                fa[v][k] = fa[fa[v][k-1]][k-1];
            }
        }
    }
}

int LCA(int x,int y){
    if(dep[x] < dep[y]) swap(x,y);
    for(int k = 20;k>=0;k--){
        if(dep[fa[x][k]] >= dep[y])
            x = fa[x][k];
    }
    if(x == y) return x;
    for(int k = 20;k>=0;k--){
        if(fa[x][k] != fa[y][x]){
            x = fa[x][k];
            y = fa[y][k];
        }
    }
    return fa[x][0];
}