AtCoder Regular Contest 102

传送门

C - Triangular Relationship

题意:
给出(n,k),现在要你求合法三元组的数量,合法是指对于一个三元组((a,b,c)),每个数都不超过(n),并且(a+b,b+c,a+c)(k)的倍数。

思路:
按照模(k)的余数来讨论即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int n, k;

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> k;
    ll ans = n / k;
    ans = ans * ans * ans;
    ll tmp = n / k + (n % k >= k / 2);
    if(k % 2 == 0) ans += tmp * tmp * tmp;
    cout << ans;
    return 0;
}

D - All Your Paths are Different Lengths

题意:
给出(L),先要求构造一个点数不超过(20),边数不超过(60)的有向图,并且满足从起点(1)到终点(n)共有(L)条路径,并且路径长度为(0,1,cdots,L-1)(Lleq 10^6)

思路:

  • 考虑二进制拆分。
  • 假设(L)的二进制的最高位为(r),那么我们首先构造出一条链的形式,有(r)个结点,并且前一个点向后一个点连边(0)(2^i(i<r))
  • 这条链即表示最高位为(0)时的所有情况。
  • 但最高位为(1)时怎么办?
  • 假设这条链是从(1)号点开始,到(n)号点结尾,并且边((i,i+1))的边权为(2^{i-1})。那么从点(i)连接一条边到(n)号结点,边权为(X),可以发现会多出一些长度为(X+0,X+1,cdots,X+2^{i-1}-1)的路径。
  • 根据这一点选择合适的(X)以及结点(i)处理即可。

代码如下:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 66;
int L;
int sum[N];
int n, m;
vector <pair<int, int> > g[N];
void add(int u, int v, int w, int op) {
    ++m;
    g[u].push_back(make_pair(v, w));
    if(op) sum[v] = sum[u] + w;
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> L;
    int r;
    for(int i = 30; i >= 0; i--) if(L >> i & 1) {
        r = i; break;
    }
    for(int i = 2; i <= r + 1; i++) {
        add(i - 1, i, 1 << (i - 2), 1);
        add(i - 1, i, 0, 0);
    }
    int X = (1 << r); --L;
    while(X <= L) {
        int p = upper_bound(sum + 1, sum + r + 1, L - X) - sum - 1;
        add(p, r + 1, X, 0);
        X += sum[p] + 1;
    }
    n = r + 1;
    cout << n << ' ' << m << '
';
    for(int u = 1; u <= n; u++) {
        for(auto it : g[u]) {
            int v = it.first, w = it.second;
            cout << u << ' ' << v << ' ' << w << '
';
        }
    }
    return 0;
}

E - Stop. Otherwise...

题意:
(n)个骰子,每个骰子上面有点数(1,2,cdots,k)
先求对于所有(x=2,3,cdots,2k),任意两个骰子的点数之和都不为(x)的状态数为多少。
两个状态为不同的方案,当且仅当至少存在一个点数(i),在两种方案中的出现次数不同。

思路:
显然可以考虑容斥来搞。

  • 对于一个点(i),如果出现了,那么说明就不能出现(x-i),在每种情况中,都有若干这种pair关系。

  • 那么在一种情况中,我们容斥掉所有不合法情况即可。

  • 怎么容斥?

  • 假设当前有(cnt)(pair)为非法的,我们直接枚举选了多少对,那么答案就是(sum(-1)^j {cntchoose j}{n-2j+k-1choose k-1})

  • 后面的组合数就是隔板法的运用,我们知道所有点数出现次数之和为(n),那么就相当于求(x_1+x_2+cdots +x_k=n,x_igeq 0)的解的个数。

这里的隔板法和平时还是有点区别,还是要看具体题目吧,怎样才算不同的情况,以及等式的存在性。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4005, MOD = 998244353;

int fac[N], inv[N];

int qp(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ans;
}

void pre() {
    fac[0] = 1;
    for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[N - 1] = qp(fac[N - 1], MOD - 2);
    for(int i = N - 2; ~i; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}

ll C(int n, int m) {
    if(n < m) return 0;
    return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}

int n, k;
int tot[N];

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    pre();
    cin >> k >> n;
    for(int i = 1; i <= k; i++) {
        ++tot[i + 1]; --tot[i + k + 1];
    }
    for(int i = 1; i <= 2 * k; i++) tot[i] += tot[i - 1];
    for(int i = 2; i <= 2 * k; i++) {
        ll ans = 0;
        int cnt = (tot[i] + 1) / 2;
        for(int j = 0, d = 1; j <= cnt; j++, d = MOD - d) {
            ans += 1ll * d * C(cnt, j) % MOD * C(n - 2 * j + k - 1, k - 1) % MOD;
            ans %= MOD;
        }
        if(ans < 0) ans += MOD;
        cout << ans << '
';
    }
    return 0;
}

F - Revenge of BBuBBBlesort!

题意:
给出一个(1)(n)的排列,现在你可以执行操作任意多次,该操作为:

  • 选择(3)个元素,满足(a_{i-1}>a_{i}>a{i+1}),那么可以将这三个数翻转。

问最后是否能将该排列变为(1,2,cdots,n)

思路:
很考察分析和观察能力的一个题。
我们可以考虑一个逆过程,就是对于(1,2,cdots,n),我们可以选择递增的三个数,将其翻转。
那么就有如下几个观察:

  • 观察1:如果以(i)为中心进行了一次翻转,那么(a_i)则不会发生改变。
  • 观察2:原问题可以划分为若干个独立的区间,每个区间我们可以单独处理。假设这个区间为([l,r]),那么(l+1,l+3,cdots,r-1)这些位置的数不会发生改变。
  • 观察3:对于(a_i),若其先向左移动,则不能再向右移动,并且可以引申:向左移动的数的相对位置不会改变。向右移动同理。

这些观察的具体证明...第一个和第三个很好证,第二个感性理解一下?
然后这个题就可以这样直接搞就行了。细节见代码吧。

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

int n;
int a[N];

void YES() {
    cout << "Yes"; exit(0);
}

void NO() {
    cout << "No"; exit(0);
}

int tmp[N], cnt;
int pos[N], dir[N];

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    int l = 1, r;
    while(1) {
        while(l <= n && a[l] == l) ++l;
        if(l > n) YES();
        r = l;
        while(1) {
            if((r - l + 1) & 1) {
                if(a[r] != r) ++r;
                else break;
            } else {
                if(a[r] == r) ++r;
                else break;
            }
            if(r > n) break;
        }
        --r; if((r - l) & 1) --r;
        if(l == r) NO();
        cnt = 0;
        for(int i = l; i <= r; i++) tmp[++cnt] = a[i];
        sort(tmp + 1, tmp + cnt + 1);
        for(int i = 1; i <= cnt; i++) if(tmp[i] != i + l - 1) NO();
        cnt = 0;
        for(int i = l; i <= r; i += 2) {
            tmp[++cnt] = a[i]; pos[a[i]] = i;
        }
        sort(tmp + 1, tmp + cnt + 1);
        for(int i = 1; i <= cnt; i++) {
            dir[pos[tmp[i]]] = 2 * i + l - 2;
        }
        int lastl = 0, lastr = n + 1;
        for(int i = l; i <= r; i += 2) {
            if(dir[i] > i) continue;
            if(dir[i] > lastl) lastl = dir[i];
            else NO();
        }
        for(int i = r; i >= l; i -= 2) {
            if(dir[i] < i) continue;
            if(dir[i] < lastr) lastr = dir[i];
            else NO();
        }
        l = r + 1;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/heyuhhh/p/11461825.html