MOBA(模板)

图论

拓扑排序

queue<int>q; // 如果题目有偏序条件,可以改为优先队列
vector<int>a;
vector<int>e[N];
void topsort() {
    for (int i = 1; i <= n; i++) {
        // 入度为0的节点入队
        if (!p[i]) { 
            q.push(i);
        }
    }
    while (!q.empty()) {
        int t = q.front();
        q.pop();
        a.push_back(t);
        for (auto &i : e[t]) {
            p[i]--;
            if (!p[i]) {
                q.push(i);
            }
        } 
    }
}

最小生成树

int n, m; // n 为节点数, m 为边数

struct node {
    int a;
    int b;
    int w;
}e[M];

bool cmp(node a, node b) {
    return a.w < b.w;
}

int kruskal() {
    int res = 0, cnt = 0;
    for (int i = 0; i <= n; i++) p[i] = i;
    sort(e + 1, e + 1 + m; cmp);
    for (int i = 1; i <= m; i++) {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        if (find(a) != find(b)) {
            join(a, b);
            res += w;
            cnt++;
        }
    }
    return res;
}

并查集

// 初始化操作
for (int i = 0; i <= n; i++) {
    p[i] = i;
}

// 查找,路径压缩
int find(int x) {
    if (x == p[x]) return x;
    return p[x] = find(p[x]);
}

// 合并操作
void join(int a, int b) {
    int aa = find(a), bb = find(b);
    if (aa == bb) return;
    p[aa] = bb;
}

单源最短路 dij 堆优化

vector<node>e[N];
void dij(int s) {
    priority_queue< PII, vector<PII>, greater<PII> >q;
    for (int i = 0; i <= n + 1; i++) {
        d[i] = INF;
        vis[i] = false; 
    }
    d[s] = 0;
    q.push({0, s});
    while (!q.empty()) {
        PII t = q.top();
        q.pop();
        int u = q.second, dis = q.first;
        if (vis[u]) continue;
        vis[u] = true;
        for (auto &i : e[u]) {
            if (d[i.v] > d[u] + i.w) {
                d[i.v] = d[u] + i.w;
                q.push({d[i.v], i.v});
            }
        } 
    } 
} 

多源最短路 费得比尔曼算法

struct Edge
{
    int a, b, c;
}edges[M];

int n, m;
int dist[N];
int last[N];

void bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);

    dist[1] = 0;
    for (int i = 0; i < n; i ++ )
    {
        memcpy(last, dist, sizeof dist);
        for (int j = 0; j < m; j ++ )
        {
            auto e = edges[j];
            dist[e.b] = min(dist[e.b], last[e.a] + e.c);
        }
    }
}

多源最短路 spfa算法

vector<node>e[N];
int spfa() {
    memset(dist, 0x3f, sizeof dist);
    d[1] = 0;
    queue<int> q;
    q.push(1);
    st[1] = true;
    while (!q.empty()) {
        int t = q.front();
        q.pop();
        st[t] = false;
        for (auto &i : e[t]) {
            if (d[i.v] > d[t] + i.w) {
                d[i.v] = d[t] + i.w;
                if (!st[i.v]) {
                    q.push(i.v);
                    st[i.v] = true;
                }
            }
        }
    }
    if (d[n] == 0x3f3f3f3f) return -1;
    return d[n];
}

判负环 spfa算法

bool fspfa() {
    // 不需要初始化dist数组
    // 原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。
    queue<int> q;
    for (int i = 1; i <= n; i ++ ) {
        q.push(i);
        st[i] = true;
    }
    while (!q.empty()) {
        int t = q.front();
        q.pop();
        st[t] = false;
        for (auto &i : e[t]) {
            if (d[i.v] > d[t] + i.w) {
                d[i.v] = d[t] + i.w;
                cnt[i.v] = cnt[t] + 1;
                if (cnt[i.v] >= n) return true;       // 如果从1号点到x的最短路中包含至少n个点(不包括自己),则说明存在环
                if (!st[i.v]) {
                    q.push(i.v);
                    st[i.v] = true;
                }
            }
        }
    }
    return false;
}

全源最短路 floyd算法

//初始化:
for (int i = 1; i <= n; i ++ )
    for (int j = 1; j <= n; j ++ )
        if (i == j) d[i][j] = 0;
        else d[i][j] = INF;

void floyd() {
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            }
        }
    }
}

全源最短路 Johnson

struct edge{
    int v,w;
};

struct node{
    int d,u;
    bool operator<(const node &a) const{ return d > a.d; }
    node(int d, int u): d(d) , u(u) {}
};

vector<vector<edge>>g;
vector<int>vis, cnt;
vector<ll>d, h;
int n, m;

bool spfa(int s){
    h.assign(n + 1, INF);
    vis.assign(n + 1, 0);
    cnt.assign(n + 1, 0);
    h[s] = 0, vis[s] = 1;
    queue<int>q;
    q.push(s);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(auto &i : g[u]){
            auto [v, w] = i;
            if(h[v] > h[u] + w){
                h[v] = h[u] + w;
                cnt[v] = cnt[u] + 1;
                if(cnt[v] > n){
                    return false;
                }
                if(!vis[v]){
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return true;
}

void dijkstra(int s){
     d.assign(n + 1, INF);
     vis.assign(n + 1, 0);
     d[s] = 0;
     priority_queue<node>q;
     q.push({0, s});
     while(!q.empty()){
        int u = q.top().u;
        q.pop();
        if(vis[u]){
            continue;
        }
        vis[u] = 1;
        for(auto &i : g[u]){
            int v = i.v , w = i.w;
            if(d[v] > d[u] + w){
                d[v] = d[u] + w;
                q.push({d[v],v});
            }
        }
     }
     return;
}

二分图 染色法判图

int n, m;
int color[N];
vector<int>e[N];

bool dfs(int u, int c){
    color[u] = c;
    for (auto &i : e[u]) {
        if (!color[i]) {
            if (!dfs(i, 3 - c)) return false;
        }
        else if (color[i] == c) return false;
    }
    return true;
}

// 判图
bool flag = true;
for (int i = 1; i <= n; i ++ ){
    if (!color[i]) {
        if (!dfs(i, 1)) {
            flag = false;
            break;
        }
    }
}
if (flag) puts("Yes");
else puts("No");

二分图 最大匹配

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
#define ll long long

vector<int>edge[505];
vector<int>match(505, 0);
vector<bool>st(505, false);

bool find(int x){
    for(auto &i : edge[x]){
        if(!st[i]){
            st[i] = true;
            if(!match[i] || find(match[i])){
                match[i] = x;
                return true;
            }
        }
    }
    return false;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n, m, e;
    cin >> n >> m >> e;
    int a, b;
    while(e--){
        cin >> a >> b;
        edge[a].push_back(b);
    }
    int sum = 0;
    // 匹配时只需要找其中一个集合
    for(int i = 1; i <= n; i++){
        st.assign(505, false);
        if(find(i))sum++;
    }
    cout << sum << "\n";
    return 0;
}

二分图 最大匹配边权 KM算法

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;

vector<int>match(50), lx(50, 0), ly(50, 0);
vector<bool>sx(50), sy(50);

int a[50][50];
int n, MIN;

bool find(int x) {
    sx[x] = true;
    for (int i = 1; i <= n; i++) {
        if (!sy[i]) {
            if (lx[x] + ly[i] == a[x][i]) {
                sy[i] = true;
                if (!match[i] || find(match[i])) {
                    match[i] = x;
                    return true;
                }
            }
            else if (lx[x] + ly[i] > a[x][i]) {
                MIN = min(MIN, lx[x] + ly[i] - a[x][i]);
            }
        }
    }
    return false;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> n;
    int x;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> x;
            a[i][j] = x;
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            lx[i] = max(lx[i], a[i][j]);       
        }
    }
    for (int i = 1; i <= n; i++) {
        while (1) {
            MIN = INF;
            sx.assign(25, false);
            sy.assign(25, false);
            if (find(i)) break;
            for (int j = 1; j <= n; j++) {
                if(!sx[j])continue;
                lx[j] -= MIN;
            }
            for (int j = 1; j <= n; j++) {
                if(!sy[j])continue;
                ly[j] += MIN;
            }
        }
    }
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += a[match[i]][i];
    }
    cout << sum << "\n";
    return 0; 
}

Tarjan算法

int n, m;
stack<int>st;
int dfn[N], low[N], in[N], scc[N], sz[N];
int dfncnt, sc;
vector<int>e[N];
int rd[N], cd[N];

void tarjan(int u) {
    dfn[u] = low[u] = ++dfncnt;
    in[u] = 1;
    st.push(u);
    for (auto &v : e[u]) {
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (in[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (low[u] == dfn[u]) {
        sc++;
        while (st.top() != u) {
            int tp = st.top();
            st.pop();
            scc[tp] = sc;
            sz[sc]++;
            in[tp] = 0;
        }
        int tp = st.top();
        st.pop();
        scc[tp] = sc;
        sz[sc]++;
        in[tp] = 0;
    }
    return;
}

树上问题之最近公共祖先

int n, m, root;
vector<int>e[N];
int dep[N], f[N][32]; 

void dfs(int u, int fa) {
    f[u][0] = fa;
    dep[u] = dep[f[u][0]] + 1;
    // 初始化:其他的祖先节点:第 2^i 的祖先节点是第 2^(i-1) 的祖先节点的第2^(i-1) 的祖先节点。
    for (int i = 1; i < 31; i++) {
        f[u][i] = f[f[u][i - 1]][i - 1];
    }
    for (auto &v : e[u]) {
        if (v == fa) continue;
        dfs(v, u);
    }
}

int lca(int x, int y) {
    if (dep[x] > dep[y]) swap(x, y);
    //令 y 和 x 在一个深度。
    int temp = dep[y] - dep[x], ans = 0;
    for (int j = 0; temp; j++, temp >>= 1) {
        if (temp & 1) y = f[y][j];
    }
    // 如果这个时候 y = x,那么 x,y 就都是它们自己的祖先。
    if (y == x) return x;
    // 不然的话,找到第一个不是它们祖先的两个点。
    for (int j = 30; j >= 0 && y != x; --j) {
        if (f[x][j] != f[y][j]) {
            x = f[x][j];
            y = f[y][j];
        }
    }
    return f[x][0];
}

树上问题之树的直径

ll ans = 0;
ll dp[N];
vector<PII>e[N];

void dfs(int u, int fa) {
    for (auto &v : e[u]) {
        if (v.first == fa) continue;
        dfs(v.first, u);
        ans = max(ans, dp[u] + dp[v.first] + v.second);
        dp[u] = max(dp[u], dp[v.first] + v.second);
    }
}

数论

素数筛

void get_primes(int n)
{
    int primes[1], cnt;     // primes[]存储所有素数  
    bool st[1];         // st[x]存储x是否被筛掉
    //上面两行开全局,1为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;
        }
    }
}

分解质因数

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 gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}

扩展欧几里得算法

// 求x, y,使得ax + by = gcd(a, b)
int exgcd(int a, int b, int &x, int &y) {
    if (!b) {
        x = 1; y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= (a/b) * x;
    return d;
}

快速幂

int qmi(int m, int k, int p) {
    int res = 1 % p, t = m;
    while (k) {
        if (k&1) res = res * t % p;
        t = t * t % p;
        k >>= 1;
    }
    return res;
}

卡特兰数

Cat[n] = C(2, n) / (n + 1);

卢卡斯定理求组合数

ll qmi(ll m, ll k, ll p) {
    ll res = 1 % p, t = m;
    while (k) {
        if (k&1) res = res * t % p;
        t = t * t % p;
        k >>= 1;
    }
    return res % p;
}
 
ll C(ll a, ll b, ll p) {
    if (a < b) return 0;
  
    ll x = 1, y = 1;  // x是分子,y是分母
    for (int i = a, j = 1; j <= b; i --, j ++ )
    {
        x = (ll)x * i % p;
        y = (ll) y * j % p;
    }
  
    return x * (ll)qmi(y, p - 2, p) % p;
}
  
ll lucas(ll a, ll b, ll 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;
}

递推求组合数

vector<vector<Z>>C(n + 1, vector<Z>(n + 1));
     
for (int i = 0; i <= n; i++) {
    C[i][0] = 1;
    for (int j = 1; j <= i; j++) {
        C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]);
    }
}

斯特林数

vector S(N + 1, vector<Z>(N + 1));

for (int i = 0; i <= N; i++) {
    S[i][0] = (i == 0);
    for (int j = 1; j <= i; j++) {
        S[i][j] = S[i - 1][j - 1] + S[i - 1][j] * j;
    }
}

数据结构

邻接表 链式前向星

int h[N], w[N], e[N], ne[N], idx;

void add(int a, int b, int c){
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++ ;
}

树状数组


int tr1[N];  // 维护a[i]前缀和
int tr2[N];  // 维护a[i] * i的前缀和

int lowbit(int x) {
    return x & (-x);
}

void add(int tr[], int x, int k) {
    for (int i = x; i <= n; i += lowbit(i)) {
        tr[i] += k;
    }
}

// 求某点值
int sum(int tr[], int x) {
    int res = 0;
    for (int i = x; i ; i -= lowbit(i)) {
        sum += tr[i];    
    }
} 

// 求区间和
int prefix_sum(int x){
    return sum(tr1, x) * (x + 1) - sum(tr2, x);
}

//操作1 求单点值
cout << sum(l) << "\n";

// 建树
add(l, d), add(r + 1, -d);

//操作2 求区间和
cout << prefix_sum(r) - prefix_sum(l - 1);

//建树        
add(tr1, l, r), add(tr2, l, l * r);
add(tr1, l + 1, - r), add(tr2, l + 1, (l + 1) * - r);

线段树


//本模板的操作为 区间乘一个x, 区间加一个x

ll tr[N * 4], b[N * 4], mul[N * 4];
ll a[N];
ll mod;

void build(int p, int l, int r){
    mul[p] = 1;
    if(l == r){
        tr[p] = a[l];
        return;
    }
    int mid = l + ((r - l) >> 1);
    build(2 * p, l, mid), build(2 * p + 1, mid + 1, r);
    tr[p] = (tr[p * 2] + tr[p * 2 + 1]) % mod;
    return;
}

void pushup(int p){
    tr[p] = (tr[p * 2] + tr[p * 2 + 1]) % mod;
    return;
}

void pushdowm(int p, int s, int t){
    int m = s + ((t - s) >> 1);
    // 乘法懒惰标记
    if(mul[p] != 1){
        mul[p * 2] = mul[p * 2] * mul[p] % mod;
        mul[p * 2 + 1] = mul[p * 2 + 1] * mul[p] % mod;
        b[p * 2] = b[p * 2] * mul[p] % mod;
        b[p * 2 + 1] = b[p * 2 + 1] * mul[p] % mod;
        tr[p * 2] = tr[p * 2] * mul[p] % mod;
        tr[p * 2 + 1] = tr[p * 2 + 1] * mul[p] % mod;
        mul[p] = 1;
    }
    // 只有加法时候的模板
    if(b[p]){
        tr[p * 2] = (b[p] * (m - s + 1) + tr[p * 2]) % mod;
        tr[p * 2 + 1] = (b[p] * (t - m) + tr[p * 2 + 1]) % mod;
        b[p * 2] = (b[p] + b[p * 2]) % mod;
        b[p * 2 + 1] = (b[p] + b[p * 2 + 1]) % mod;
        b[p] = 0;
    }
    return;
}

void update1(int l, int r, ll c, int s, int t, int p){
    if(l <= s && t <= r){
        tr[p] += (t - s + 1) * c, b[p] += c;
        tr[p] %= mod, b[p] %= mod;
        return;
    }  
    pushdowm(p, s, t);
    int m = s + ((t - s) >> 1);
    if (l <= m) update1(l, r, c, s, m, p * 2);
    if (r > m) update1(l, r, c, m + 1, t, p * 2 + 1);
    pushup(p);
    return;
}

void update2(int l, int r, ll c, int s, int t, int p){
    if(l <= s && t <= r){
        tr[p] *=  c, b[p] *= c, mul[p] *= c;
        tr[p] %= mod, b[p] %= mod, mul[p] %= mod;
        return;
    }  
    pushdowm(p, s, t);
    int m = s + ((t - s) >> 1);
    if (l <= m) update2(l, r, c, s, m, p * 2);
    if (r > m) update2(l, r, c, m + 1, t, p * 2 + 1);
    pushup(p);
    return;
}

ll getsum(int l, int r, int s, int t, int p) {
    if (l <= s && t <= r) return tr[p];
    int m = s + ((t - s) >> 1);
    pushdowm(p, s, t);
    ll sum = 0;
    if (l <= m) sum += getsum(l, r, s, m, p * 2);
    sum %= mod;
    if (r > m) sum += getsum(l, r, m + 1, t, p * 2 + 1);
    return sum % mod;
}

模拟散列表之开放寻址法

ull find(ull x) {
    ull t = (x % N + N) % N;
    while (h[t] != INF && h[t] != x) {
        t++ ;
        if (t == N) t = 0;
    }
    return t;
}

字符串HASH

#define ull unsigned long long
const int N = 1e5 + 11;
const ull P = 131;

ull h[N], p[N], a[N];
string s;

ull get(int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}

ull Hash(){
    int k = s.size();
    p[0] = 1;
    for (int i = 1; i <= k; i ++ )
    {
        h[i] = h[i - 1] * P + (ull)s[i - 1];
        p[i] = p[i - 1] * P;
    }
    return get(1, k);
}

//如果爆ll :
#define ull unsigned long long
const ull P = 233;
const int N = 2e6 + 10;
const ull mod = 99989397;

ull h[N], p[N], a[N];
string s;

ull get(int l, int r)
{
    return (h[r] - h[l - 1] * p[r - l + 1] % mod + mod) % mod;
}

ull Hash(){
    int k = s.size();
    p[0] = 1;
    for (int i = 1; i <= k; i ++ )
    {
        h[i] = ((h[i - 1] * P) % mod + (ull)s[i - 1]) % mod;
        p[i] = (p[i - 1] * P) % mod;
    }
    return get(1, k);
}

Trie树

int nex[N][65], cnt, a[N];
int n, m;

int getval(char c) {
    if (c >= 'A' && c <= 'Z') {
        return (c - 'A');
    }
    else if (c >= 'a' && c <= 'z') {
        return (c - 'a' + 26);
    }
    else {
        return (c - '0' + 52);
    }
}

void insert(string s) {
    int p = 0;
    for (int i = 0; i < s.size(); i++) {
        int c = getval(s[i]);
        if (!nex[p][c]) nex[p][c] = ++cnt;
        p = nex[p][c];
        a[p]++;
    }
    return;
}

int find(string s) {
    int p = 0;
    for (int i = 0; i < s.size(); i++) {
        int c = getval(s[i]);
        if (!nex[p][c]) return 0;
        p = nex[p][c];
    }
    return a[p];
}

Trie数之最大异或与最小异或

#define ll long long
const int N = 1e5 + 10;
#define PII pair<int, int>
const ll INF = 0x3f3f3f3f;

int nex[N * 32][2], a[N * 32], ep[N * 32];
int cnt;

void insert(ll s) {
	int p = 0;
	for (int i = 31;  i >= 0 ; i--) {
		int c = s >> i & 1;
		if (nex[p][c] == -1)
			nex[p][c] = ++cnt;
		p = nex[p][c];
	}
	return;
}

//最大异或
ll find1(ll s) {
	ll ans = s;
	int p = 0;
	for (int i = 31; i >= 0; i--) {
		int c = s >> i & 1;
		if (nex[p][!c] != -1) {
			ans ^= ((!c) << i);
			p = nex[p][!c];
		} else {
			ans ^= (c << i);
			p = nex[p][c];
		}
	}
	return ans;
}

// 最小异或
ll find2(ll s) {
	ll ans = s;
	int p = 0;
	for (int i = 31; i >= 0; i--) {
		int c = s >> i & 1;
		if (nex[p][c] != -1) {
			ans ^= ((c) << i);
			p = nex[p][c];
		} else {
			ans ^= ((!c) << i);
			p = nex[p][!c];
		}
	}
	return ans;
}

void solve(int u) {
	memset(nex, -1, sizeof nex);
	cnt = 0;
	int n;
	cin >> n;
	ll sum = 0;
	ll ans1 = -INF;
	ll ans2 = INF;
	insert(0);
	ll x;
	while (n--) {
		cin >> x;
		sum ^= x;
		ans2 = min(ans2, find2(sum));
		insert(sum);
		ans1 = max(ans1, find1(sum));
	}
	cout << "Case " << u << ": " << ans1 << " " << ans2 << "\n";
	return;
}

主席树

const int maxn = 1e5;  // 数据范围
int tot, n, m;
int sum[(maxn << 5) + 10], rt[maxn + 10], ls[(maxn << 5) + 10],
    rs[(maxn << 5) + 10];
int a[maxn + 10], ind[maxn + 10], len;

int getid(const int &val) {  // 离散化
  return lower_bound(ind + 1, ind + len + 1, val) - ind;
}

int build(int l, int r) {  // 建树
  int root = ++tot;
  if (l == r) return root;
  int mid = l + r >> 1;
  ls[root] = build(l, mid);
  rs[root] = build(mid + 1, r);
  return root;  // 返回该子树的根节点
}

int update(int k, int l, int r, int root) {  // 插入操作
  int dir = ++tot;
  ls[dir] = ls[root], rs[dir] = rs[root], sum[dir] = sum[root] + 1;
  if (l == r) return dir;
  int mid = l + r >> 1;
  if (k <= mid)
    ls[dir] = update(k, l, mid, ls[dir]);
  else
    rs[dir] = update(k, mid + 1, r, rs[dir]);
  return dir;
}

int query(int u, int v, int l, int r, int k) {  // 查询操作
  int mid = l + r >> 1,
      x = sum[ls[v]] - sum[ls[u]];  // 通过区间减法得到左儿子中所存储的数值个数
  if (l == r) return l;
  if (k <= x)  // 若 k 小于等于 x ,则说明第 k 小的数字存储在在左儿子中
    return query(ls[u], ls[v], l, mid, k);
  else  // 否则说明在右儿子中
    return query(rs[u], rs[v], mid + 1, r, k - x);
}

动态规划DP

二进制优化多重背包


struct node
{
    int v;
    int w;
}a[N];

//v代表价值,w代表重量,m代表件数
int v[N],w[N],m[N];
int f[N];

for(int i = 1; i <= n; i++){
    int cnt = 1;
    for(int j = 1; j <= m[i]; j *= 2, cnt++){
        a[cnt].v = v[i] * j;
        a[cnt].w = w[i] * j;
        m[i] -= j;
    }
    if(m[i] > 0){
        a[cnt].v = m[i] * v[i];
        a[cnt++].w = m[i] * w[i];  
    }
    for(int j = 1; j < cnt; j++){
        for(int k = W; k >= a[j].w; k--){
            f[k] = max(f[k] , f[k - a[j].w] + a[j].v);
        }
    }
}

严格单调上升子序列

f[0] = -0x3f3f3f3f;
int len = 0;
int mid;
int ans;
for(int i = 0; i < cnt; i++){
    int l = 0, r = len;
    while(l < r){
        mid = (l + r + 1) / 2;
        if(f[mid] < a[i]){
            l = mid;
        }
        else{
            r =  mid - 1;
        }
    }
    len = max(len,r + 1);
    f[r + 1] = a[i];
}

杂项

取模板子(源自jiangly)


using ll = long long;

//快速幂
template<class T>
constexpr T qmi(T a, ll b) {
    T res {1};
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}
 
constexpr ll mul(ll a, ll b, ll p) {
    ll res = a * b - ll(1.L * a * b / p) * p;
    res %= p;
    if (res < 0) {
        res += p;
    }
    return res;
}
 
template<ll P>
struct MInt {
    ll x;
    constexpr MInt() : x {0} {}
    constexpr MInt(ll x) : x {norm(x % getMod())} {}
     
    static ll Mod;
    constexpr static ll getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(ll Mod_) {
        Mod = Mod_;
    }
    constexpr ll norm(ll x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr ll val() const {
        return x;
    }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        return power(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        if (getMod() < (1ULL << 31)) {
            x = x * rhs.x % int(getMod());
        } else {
            x = mul(x, rhs.x, getMod());
        }
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & {
        return *this *= rhs.inv();
    }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
        ll v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
        return os << a.val();
    }
    friend constexpr bool operator==(MInt lhs, MInt rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) {
        return lhs.val() != rhs.val();
    }
    friend constexpr bool operator<(MInt lhs, MInt rhs) {
        return lhs.val() < rhs.val();
    }
};
 
template<>
ll MInt<0>::Mod = 998244353;
constexpr int P = 998244353;//设置模数
using Z = MInt<P>;

高精度模板(源自linzy)

const int MAXN = 10087;// 字符大小 , 没压位
struct BigInt
{
	int len, s[MAXN];
	BigInt () // 初始化
	{
		memset(s, 0, sizeof(s));
		len = 1;
	}
	BigInt (int num) { *this = num; }
	BigInt (const char *num) { *this = num; } //让this 指 针 指 向 当 前 字 符 串
	BigInt operator = (const int num)
	{
		char s[MAXN];
		sprintf(s, "%d", num); //sprintf 函 数 将 整 型 映 到 字 符 串 中
		*this = s;
		return *this; // 再 将 字 符 串 转 到 下 面 字 符 串 转 化 的 函 数 中
	}
	BigInt operator = (const char *num)
	{
		for(int i = 0; num[i] == '0'; num++) ; // 去前导 0
		len = strlen(num);
		for(int i = 0; i < len; i++) s[i] = num[len-i-1] - '0'; // 反着存
		return *this;
	}
	BigInt operator + (const BigInt &b) const // 对应 位 相加 , 最为简单
	{
		BigInt c;
		c.len = 0;
		for(int i = 0, g = 0; g || i < max(len, b.len); i++)
		{
			int x = g;
			if(i < len) x += s[i];
			if(i < b.len) x += b.s[i];
			c.s[c.len++] = x % 10; // 关 于 加 法 进 位
			g = x / 10;
		}
		return c;
	}
	BigInt operator += (const BigInt &b) // 如上 文 所说 , 此 类 运 算 符 皆 如 此 重 载
	{
		*this = *this + b;
		return *this;
	}
	void clean() // 由 于 接 下 来 的 运 算 不 能 确 定 结 果 的 长 度 , 先 大 而 估 之 然 后 再 查
	{
		while(len > 1 && !s[len -1]) len --; // 首位部分 ‘0’ 故 删 除 该 部 分 长 度
	}
	BigInt operator * (const BigInt &b) // 乘 法 重 载 在 于 列 竖 式 , 再 将 竖 式 中 的 数 转 为 抽 象 , 即 可  出 运 算 法 则 。
	{
		BigInt c;
		c.len = len + b.len;
		for(int i = 0; i < len; i++)
		{
			for(int j = 0; j < b.len; j++)
			{
				c.s[i+j] += s[i] * b.s[j];// 不 妨 列 个 竖 式 看 一 看
			}
		}
		for(int i = 0; i < c.len; i++) // 关于进位 , 与加 法 意同
		{
			c.s[i+1] += c.s[i]/10;
			c.s[i] %= 10;
		}
		c.clean(); // 我 们 估 的 位 数 是 a+b 的长度和 , 但 可 能 比 它 小 (1*1 = 1)
		return c;
	}
	BigInt operator *= (const BigInt &b)
	{
		*this = *this * b;
		return *this;
	}
	BigInt operator - (const BigInt &b) // 对应 位 相减 , 加 法 的 进 位 改 为 借 1
	{ // 不考 虑 负数
		BigInt c;
		c.len = 0;
		for(int i = 0, g = 0; i < len; i++)
		{
			int x = s[i] - g;
			if(i < b.len) x -= b.s[i]; // 可 能 长 度 不 等

			if(x >= 0) g = 0; // 是 否 向 上 移 位 借 1
			else
			{
				g = 1;
				x += 10;
			}
			c.s[c.len++] = x;
		}
		c.clean();
		return c;
	}
	BigInt operator -= (const BigInt &b)
	{
		*this = *this - b;
		return *this;
	}
	BigInt operator / (const BigInt &b) // 运 用 除 是 减 的 本 质 , 不停地减 , 直 到 小 于 被 减 数
	{
		BigInt c, f = 0; // 可 能 会 在 使 用 减 法 时 出 现 高 精 度 运 算
		for(int i = len -1; i >= 0; i--) // 正常顺序 , 从 最 高 位 开 始
		{
			f = f*10; // 上 面 位 的 剩 余 到 下 一 位 *10
			f.s[0] = s[i]; // 加上 当 前位
			while(f >= b)
			{
				f -= b;
				c.s[i]++;
			}
		}
		c.len = len; // 估最长位
		c.clean();
		return c;
	}	
	BigInt operator /= (const BigInt &b)
	{
		*this = *this / b;
		return *this;
	}
	BigInt operator % (const BigInt &b) // 取 模 就 是 除 完 剩 下 的
	{
		BigInt r = *this / b;
		r = *this - r*b;
		r.clean();
		return r;
	}
	BigInt operator %= (const BigInt &b)
	{
		*this = *this % b;
		return *this;
	}
	bool operator < (const BigInt &b) // 字 符 串 比 较 原 理
	{
		if(len != b.len) return len < b.len;
		for(int i = len -1; i != -1; i--)
		{
			if(s[i] != b.s[i]) return s[i] < b.s[i];
		}
		return false;
	}
	bool operator > (const BigInt &b) // 同理
	{
		if(len != b.len) return len > b.len;
		for(int i = len -1; i != -1; i--)
		{
			if(s[i] != b.s[i]) return s[i] > b.s[i];
		}
		return false;
	}
	bool operator == (const BigInt &b)
	{
		return !(*this > b) && !(*this < b);
	}
	bool operator != (const BigInt &b)
	{
		return !(*this == b);
	}
	bool operator <= (const BigInt &b)
	{
		return *this < b || *this == b;
	}
	bool operator >= (const BigInt &b)
	{
		return *this > b || *this == b;
	}
	string str() const // 将 结 果 转 化 为 字 符 串 ( 用于输出 )
	{
		string res = "";
		for(int i = 0; i < len; i++) res = char(s[i]+'0')+res;
		return res;
	}
};


istream& operator >> (istream &in, BigInt &x) // 重载 输 入流
{
	string s;
	in >> s;
	x = s.c_str(); //string 转化为 char[]
	return in;
}

ostream& operator << (ostream &out, const BigInt &x) // 重载 输出 流
{
	out << x.str();
	return out;
}

二分


bool check(int x) {};

int l, r; //左右边界
int mid, ans = -1;
while (l <= r) {
    mid = l + ((r - l) >> 1);
    if (check()) {
        //如果求最小答案
        r = mid - 1;
        ans = mid;
        //如果求最大答案
        l = mid + 1;
        ans = mid
    } else {
        //如果求最小答案
        l = mid + 1;
        //如果求最大答案
        r = mid - 1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值