2021牛客寒假算法基础集训营 6 题解

官方题解传送门

B 系数

这一题的重点就在于1和-2在模3下是同余的,所以这一题的((x^2+x+1)^2)((x^2-2x+1)^2)在3下是同余的,题目就转化成了求((x^2-1)^{2n})的第(k)项系数,用二项式定理可知,就是((-1)^{k}C(2n, k)),用Lucas求解即可

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
#define fi first
#define se second
#define mp make_pair
#define sz(v) ((int)(v).size())
#define debug(a) cout << #a << " = " << a << endl;
typedef long long ll;
typedef pair <int, int> P;
vector < P > edge[N];
const ll MOD = 1e9 + 7;
int n, m;
int a[N];
int c[3][3] = {1, 0, 0, 1, 1, 0, 1, 2, 1};
int Lucas(ll n, ll m, ll p) {
    if (m > n)  return 0;
    else if (m >= 3 || n >= 3)  return Lucas(n / p, m / p, p) * c[n % p][m % p] % p;
    else   return c[n][m];
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    while (T--) {
        ll n, k;
        cin >> n >> k;
        if (k & 1)  cout << 2 * Lucas(2 * n, k, 3) % 3 << endl;
        else cout << Lucas(2 * n, k, 3) << endl;
    }
    return 0;
}

E 网格

DP,你可以发现,对于行和列是可以分开看的,因为一定要是90°,所以无论选往左边还是右边,对选上下都没有影响,直接DP即可

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
#define fi first
#define se second
#define mp make_pair
#define sz(v) ((int)(v).size())
#define debug(a) cout << #a << " = " << a << endl;
typedef long long ll;
typedef pair <int, int> P;
vector < P > edge[N];
const ll MOD = 1e9 + 7;
int n, m;
int a[N][N];
ll dp[N][2];
inline int w(int x) {
    int ans = x;
    while (x) {
        if (x & 1)  ans++;
        x = x >> 1;
    } 
    return ans;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> a[i][j];
    ll ans = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 2; j <= m; j++) {
            dp[j][0] = max(dp[j - 1][1] + w(a[i][j - 1] ^ a[i][j]), dp[j - 1][0]);
            dp[j][1] = max(dp[j - 1][0], dp[j - 1][1]);  
            if (j == m) ans += max(dp[j][0], dp[j][1]);
        }
    for (int i = 1; i <= m; i++)
        for (int j = 2; j <= n; j++) {
            dp[j][0] = max(dp[j - 1][1] + w(a[j - 1][i] ^ a[j][i]), dp[j - 1][0]);
            dp[j][1] = max(dp[j - 1][0], dp[j - 1][1]);  
            if (j == n) ans += max(dp[j][0], dp[j][1]);
        }
    cout << ans << endl;
    return 0;
}

F 组合数问题

oeis

G 机器人

刚开始就想到了贪心,但是没注意到必须开__int128,所以找不到bug还以为是自己贪心错了,然后就是用这个式子排序就行了(a_{j}b_i+b_j<a_{i}b_j+b_i),我刚开始担心这个会不会形成环,但是显然,把这个式子转化成(frac{a_j}{b_j}+frac{1}{b_i}<frac{a_i}{b_i}+frac{1}{b_j}),等于(frac{a_j}{b_j}-frac{1}{b_j}<frac{a_i}{b_i}-frac{1}{b_i}),显然不可能成环
下面这个是状压写法

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
#define fi first
#define se second
#define mp make_pair
#define sz(v) ((int)(v).size())
#define debug(a) cout << #a << " = " << a << endl;
typedef long long ll;
typedef __int128 lll;
typedef pair <int, int> P;
vector < P > edge[N];
const ll MOD = 1e9 + 7;
int n;
int a[N], b[N];
template < class x >
inline void read(x &ans) {
	static char buf = getchar();
	register int neg = 1;
	ans = 0;
	for (; !isdigit(buf); buf = getchar())
		if (buf == '-')	neg = -1;
	for (; isdigit(buf); buf = getchar())
		ans = ans * 10 + buf - '0';
	ans *= neg;
}
template < class x >
inline void print(x ans) {
    if (ans < 0) 
        putchar('-'), ans = -ans;
    if (ans >= 10)  print(ans / 10);
    putchar(ans % 10 + '0');
}
lll dp[1 << 22], x;
lll dfs(int state) {
    if (!state) return x;
    if (dp[state])  return dp[state];
    for (int i = 0; i < n; i++)
        if (state & (1 << i))
            dp[state] = max(dp[state], dfs(state ^ (1 << i)) * a[i + 1] + b[i + 1]);
    return dp[state];   
}
int main() {
    read(n); read(x);
    for (int i = 1; i <= n; i++)
        read(a[i]), read(b[i]);
    print(dfs((1 << n) - 1));
    return 0;
}

H 动态最小生成树

本场我最喜欢的题目(?),想了好久就想出来了带修莫队+LCT维护,但是复杂度太高了,而且很麻烦,场上没有想出来,最后看到题解,用线段树维护这个区间的生成树,然后pushup的过程就是归并排序形成新的生成树的过程,可以用类似反证法的方式证明,新的区间的最小生成树所用的边一定是两个子区间最小生成树的边的集合中的边

#include <bits/stdc++.h>
using namespace std;
const int N = 5 * 1e4 + 10;
const int M = 210;
#define fi first
#define se second
#define mp make_pair
#define sz(v) ((int)(v).size())
#define debug(a) cout << #a << " = " << a << endl;
typedef long long ll;
#define int ll
typedef pair <int, int> P;
struct node {
    int u, v, len;
} edge[N];
int n, m, q;
int fa[M];
int find(int a) {
    return a == fa[a] ? a : fa[a] = find(fa[a]);
}
struct SegmentTree {
    int e[N << 3][M], ans[M], ans1[M];
    inline void pushup(int * o, int *x, int * y) {
        int p1 = 1, p2 = 1, p = 1;
        for (int i = 0; i <= n; i++)   fa[i] = i, o[i] = 0; //这里把o初始化为0,因为不一定能形成一颗生成树
        while (p < n && (x[p1] || y[p2])) {
            if (edge[x[p1]].len < edge[y[p2]].len) {
                int u = find(edge[x[p1]].u), v = find(edge[x[p1]].v);
                if (u != v) fa[u] = v, o[p++] = x[p1];
                p1++;
            }  
            else {
                int u = find(edge[y[p2]].u), v = find(edge[y[p2]].v);
                if (u != v) fa[u] = v, o[p++] = y[p2];
                p2++;
            } 
        }
    }
    void query(int o, int l, int r, int L, int R) {
        if (l > R || r < L)	return ;
        if (L <= l && r <= R) {
            memcpy(ans1, ans, sizeof(ans));
            pushup(ans, ans1, e[o]);
            return ;
        }
        int mid = (l + r) / 2;
        query(o * 2, l, mid, L, R); 
        query(o * 2 + 1, mid + 1, r, L, R);
    }
    void modify(int o, int l, int r, int t) {
        if (l == r)
            return ;
        int mid = l + r >> 1;
        if (t <= mid) modify(o * 2, l, mid, t); 
        else modify(o * 2 + 1, mid + 1, r, t);
        pushup(e[o], e[o * 2], e[o * 2 + 1]);
    }
    void build(int o, int l, int r) {
        if (l == r) {
            e[o][1] = l;
            return ;
        }
        int mid = (l + r) >> 1;
        build(o * 2, l, mid); 
        build(o * 2 + 1, mid + 1, r);
        pushup(e[o], e[o * 2], e[o * 2 + 1]);
    }
} a;
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> q;
    for (int i = 1; i <= m; i++)
        cin >> edge[i].u >> edge[i].v >> edge[i].len;
    edge[0] = (node) {0, 0, (1 << 30)}; //作为边界
    a.build(1, 1, m);
    while (q--) {
        int opt;
        cin >> opt;
        if (opt == 1) {
            int x, y, z, t;
            cin >> x >> y >> z >> t;
            edge[x] = (node) {y, z, t};
            a.modify(1, 1, m, x);
        }
        else {
            int l, r;
            cin >> l >> r;
            a.query(1, 1, m, l, r);
            ll ans = 0;
            if (!a.ans[n - 1] && n != 1)  cout << "-1" << endl; //特判一下不然n=1会出错
            else {
                for (int i = 1; i < n; i++)
                    ans += edge[a.ans[i]].len;
                cout << ans << endl;
            }
            for (int i = 0; i <= n; i++)    a.ans[i] = 0; //要清空
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/cminus/p/14464527.html