acm暑期集训_2020.08.01

acm暑期集训_2020.08.01

任务清单

1、每周做两道比较难的题目,写把自闭好久的牛客第六场B题补了吧
2、补一道CF2000+的题目。

2020牛客暑期多校训练营(第六场)Binary Vector

题目链接:https://ac.nowcoder.com/acm/contest/5671/B

1、涉及知识点:线性无关,如果一个矩阵是满秩的(N个向量组成的空间秩为N),则说明该N个向量是线性无关的。
2、该题对于每一项公式如下,考虑加入第i个向量时,总情况数2^n,且不能由之前已经加入的若干向量构成,之前已经有i-1个向量,共有2^(i-1)种组合(前(i-1)个向量取与不取都能确定一个线性相关向量),于是第i个向量的可加入情况数就是 2^n - 2^(i-1)。(在此题目相加MOD 2,加法可以看作是异或,即这个向量不能等于之前向量的任意组合情况的异或和)。
在这里插入图片描述
在这里插入图片描述

方法2:
acm暑期集训_2020.08.01_第1张图片
通过观察可以发现,分子可以由n组有规律的二进制数表示,现在的问题就变成了如何在O(n)的时间复杂度内,求出所有f的值,可以同化分母一次计算。

#include 
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
ll qmi(ll a, ll b)
{
    ll r = 1;
    while (b)
    {
        if (b & 1)
            r = r * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return r;
}
ll inv(ll x)
{
    return qmi(x, MOD - 2);
}

vector<int> Ans;

int main()
{
    Ans.push_back(0);
    int inv_2 = inv(2);
    int inv_zz = qmi(inv_2, 20000000);
    ll p = 1, q = 1, ta = 0, np = qmi(2, 20000000), tp = 0;
    for (int i = 1; i <= 20000000; i++)
    {
        np = np * inv_2 % MOD;
        tp = (tp + np) % MOD;
        p = p * tp % MOD;
        q = q * inv_zz % MOD;
        ta = ta ^ (p * q % MOD);
        Ans.push_back(ta);
        // cout << ta << endl;
    }
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int x;
        scanf("%d", &x);
        printf("%d\n", Ans[x]);
    }
    return 0;
}

Educational Codeforces Round 91(启发式合并、并查集)

题目链接:https://codeforces.com/problemset/problem/1380/E

题目大意:题意是汉诺塔的简化玩法,在原有规则上,简化移动方式,一次可以移动任意一段,合并为一个需要多少次操作。多个询问是,在每次将两个塔合并操作之后,还需要多少次操作。
答案的计算方法即是看1~n中相邻的数字是否在一个塔(集合)中,考虑是用并查集实现。
在这里,首先数出初始状态的ans,然后每次合并操作的时候,去看较小的集合里面,有多少个(数的前或者后)属于较大的集合。存在一对(i-1,i)或者(i,i+1)都对ans - -。
考虑到每次合并操作都对较小的集合进行操作,这样启发式合并总操作数就不会超过nlogn。

#include 

using namespace std;
int n, m, ans;
int head[200007];
int f[200007];
int p[200007];
vector<int> e[200007];

int find(int x) {
    return f[x] == x ? x : f[x] = find(f[x]);
}

// y -> x
void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x != y) {
        f[y] = x;
        p[x] += p[y];
    }
}

int main() {
    scanf("%d%d", &n, &m);
    ans = n - 1;
    for (int i = 0; i <= m + 1; i++) {
        f[i] = i;
        p[i] = 1;
    }
    for (int i = 1; i <= n; i++) {
        int x;
        scanf("%d", &x);
        e[x].push_back(i);
        head[i] = x;
        if (head[i] == head[i - 1]) {
            ans--;
        }
    }
    printf("%d\n", ans);
    m--;
    while (m--) {
        int x, y;
        scanf("%d%d", &x, &y);
        x = find(x);
        y = find(y);
        vector<int> *a = &e[x], *b = &e[y];
        if (p[x] < p[y]) {
            swap(a, b);
            swap(x, y);
        }
        // x size is bigger y->x
        for (int i: *b) {
            if (find(head[i - 1]) == x) {
                ans--;
            }
            if (find(head[i + 1]) == x) {
                ans--;
            }
            a->push_back(i);
        }
        unite(x, y);
        printf("%d\n", ans);
    }
    return 0;
}

你可能感兴趣的:(acm暑期集训_2020.08.01)