Educational Codeforces Round 86 (Rated for Div. 2)

传送门

A. Road To Zero

贪心。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/26 22:36:10
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
int x, y, a, b;
 
void run() {
    cin >> x >> y >> a >> b;
    if(x > y) swap(x, y);
    ll ans = 0;
    if(1ll * x * y < 0) {
        ans += 1ll * (-x) * a + 1ll * y * a;
    } else if(1ll * x * y == 0) {
        ans += 1ll * y * a;   
    } else {
        int d = abs(x - y);
        ans += 1ll * d * a;
        if (x < 0) x += d;
        else y -= d;
        x = abs(x);
        ll res = min(1ll * x * b, 2ll * x * a);
        ans += res;
    }
    cout << ans << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

B. Binary Period

像构造\(010101...\)这样构造即可。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/26 22:45:50
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
void run() {
    string s; cin >> s;
    int len = s.length();
    int cnt = 0;
    for (int i = 0; i < len; i++) {
        if (s[i] == '0') ++cnt;
    }
    if (cnt == 0 || cnt == len) {
        cout << s << '\n';
        return;   
    }
    int cnt0 = cnt, cnt1 = len - cnt;
    string res = "";
    for (int i = 0; i < len; i++) {
        res += "01";
    }
    cout << res << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

C. Yet Another Counting Problem

题意:
给出\(a,b\leq 200\),然后给出\(q,q\leq 500\)个询问,每组询问给出区间\([l,r],l,r\leq 10^{18}\),求出满足\(l\leq x\leq r\)\(((x \bmod a) \bmod b) \ne ((x \bmod b) \bmod a)\)

思路:
显然询问我们可以拆分为两个前缀的询问。
对于题目中给出的式子,现在\(x\)\(a\cdot b\)一个周期,我们用数组\(sum\)记录一个周期中的前缀信息:\(sum_i\)表示\(0\)~\(i\)中有多少个数不满足上述条件。
对于一次询问\(x\),我们将\([0,x]\)拆分为若干个\([0,a\cdot b-1]\)这样的区间和剩下的区间,直接利用预处理信息计算即可。
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/27 16:18:53
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;

int a, b, q;
int sum[N];

void run() {
    cin >> a >> b >> q;
    int n = a * b;
    for (int i = 0; i < n; i++) {
        if (i) sum[i] = sum[i - 1];
        if ((i % a) % b != (i % b) % a) ++sum[i];
    }
    auto calc = [&](ll r) {
        return r / n * sum[n - 1] + sum[r % n];
    };
    while (q--) {
        ll l, r; cin >> l >> r;
        ll ans = calc(r) - calc(l - 1);
        cout << ans << ' ';   
    }
    cout << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

Bonus: \(a,b\)限制改为\(a,b\leq 10^9\)
做法如下:
我们考虑将式子变形(不妨\(a<b\)):

\[\begin{aligned} ((x\bmod a)\bmod b)&=((x\bmod b)\bmod a)\\ (x\bmod a)&=((x\bmod b)\bmod a)\\ x-x\%b&\equiv 0(\bmod a)\\ b\cdot \lfloor\frac{x}{b}\rfloor&\equiv 0(\bmod a) \end{aligned} \]

此时\(0\leq x\leq r\),我们处理一个询问的答案。
观察上面的式子,只有左边为\(lcm(a,b)\)的倍数时才为合法答案,那么假设左边为\(t\)\(lcm\),就有\(\displaystyle b\cdot \lfloor\frac{x}{b}\rfloor=t\cdot \frac{ab}{g}\),化简为\(\displaystyle \lfloor\frac{x}{b}\rfloor=t\cdot \frac{a}{g}=t\cdot a'\)
显然\(t\)的个数容易求,即为\(\displaystyle \frac{\lfloor\frac{x}{b}\rfloor}{a'}+1\),当然对于每个\(t\)\(x\)\(b\)种取值情况。
同时要注意一些边界情况,\(x\)不一定刚好取满\(b\)
细节见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/27 17:26:05
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;

void run() {
    int a, b, q;
    cin >> a >> b >> q;
    if (a > b) swap(a, b);
    int g = __gcd(a, b);
    a /= g;
    auto calc = [&] (ll x) {
        ll now = x / b;
        ll res = (now / a + 1) * b;
        if (now % a == 0) {
            (res -= b) += x % b + 1;   
        }
        return res;
    };
    while (q--) {
        ll l, r; cin >> l >> r;
        ll ans = r - l + 1;
        ans -= calc(r) - calc(l - 1);
        cout << ans << ' ';
    }
    cout << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

D. Multiple Testcases

题意:
给出\(n\)个数,每个数权值不超过\(k\),现在要从这些数中选出一些组成若干个序列,要求每个数恰好被选入一个序列之中。
并且满足限制:\(c_i\)表示不小于\(i\)的数的个数,对于\(1\leq i\leq k\)都要满足。
现在问最少构成多少个序列。
保证\(c_1\geq c_2\geq \cdots \geq c_k\)

思路:
最简单的想法,我们从后往前贪心来选,每次找到一个合适的位置然后将其删除添加入序列中,反复执行这个过程即可。
因为我们需要查找对应位置,并且要删除操作,所以用个\(set\)维护原序列剩下的数。
时间复杂度\(O(nlog^2n)\)
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/26 23:46:45
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5;

int n, k;
int m[N], c[N];

void run() {
    cin >> n >> k;
    multiset <int> s;
    for (int i = 1; i <= n; i++) {
        cin >> m[i];   
        s.insert(m[i]);
    }
    for (int i = 1; i <= k; i++) {
        cin >> c[i];   
    }
    vector <vector<int>> ans;
    while(sz(s)) {
        vector <int> res;
        while(1) {
            int l = 1, r = k + 1, mid;
            auto chk = [&] (int x) {
                auto it = s.lower_bound(x);
                if(it != s.end() && c[*it] >= sz(res) + 1) {
                    return true;
                }
                return false;
            };
            while(l < r) {
                mid = (l + r) >> 1;
                if(chk(mid)) l = mid + 1;
                else r = mid;
            }   
            auto it = s.lower_bound(l - 1);
            if(it == s.end() || *it != l - 1) break;
            s.erase(it);
            res.push_back(l - 1);
        }
        ans.push_back(res);
    }
    cout << sz(ans) << '\n';
    for (int i = 0; i < sz(ans); i++) {
        cout << sz(ans[i]);
        for (auto it : ans[i]) {
            cout << ' ' << it;
        }   
        cout << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

这个题的时间复杂度还可以进一步优化。
我们注意到假设最后的答案为\(k\),那么将原数组像\(1,2,\cdots,k,1,2\cdots,k,1,\cdots\)这样进行分组肯定符合条件。
所以我们二分答案,直接将原序列像上面进行分组然后check即可,时间复杂度为\(O(nlogn)\)
这个题还可以做到\(O(n)\)。我们现在只需要快速求出最小的\(k\)即可。
那么遍历一遍,对每个位置求出按照当前位置作为开头最小的分段数,这里面取最大值即是答案。
假设\(i\)得到\(x\)\(j,i<j\)得到\(y\),若\(y\leq x\),那么分为\(x\)组也可以满足条件;否则分\(y\)组才行,分\(y\)组也满足\(i\)位置的限制。
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/27 16:10:31
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5;

int n, k;
int m[N], c[N], cnt[N], suf[N];

void run() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> m[i];
        ++cnt[m[i]];
    }
    sort(m + 1, m + n + 1);
    for (int i = k; i >= 1; i--) {
        suf[i] = suf[i + 1] + cnt[i];   
    }
    for (int i = 1; i <= k; i++) {
        cin >> c[i];   
    }
    int res = 0;
    for (int i = 1; i <= k; i++) {
        res = max(res, (suf[i] - 1) / c[i] + 1);   
    }
    cout << res << '\n';
    vector <vector <int>> ans(res);
    for (int i = 1; i <= n; i++) {
        ans[(i - 1) % res].push_back(m[i]);
    }
    for (int i = 0; i < res; i++) {
        cout << sz(ans[i]);
        for (auto it : ans[i]) cout << ' ' << it;
        cout << '\n';   
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

感觉\(O(nlogn)\)的做法是最自然的,“性价比”也比较高。

E. Placing Rooks

题意:
给出一个\(n\cdot n\)的棋盘,现在要在棋盘上放置\(n\)个棋子,每个棋盘能够攻击到在同一行、同一列的格子。
现在问满足如下条件的棋子放置总情况个数:

  • 棋盘每个格子都能被攻击到;
  • 恰好有\(k\)对棋子能互相攻击,一对棋子能互相攻击的条件是这对棋子位于同一行或者同一列,并且中间没有其它棋子。

比如下图为\(n=3,k=2\)的一种合法情况:

思路:
因为保证每个格子都处于攻击之下,所以每一行/每一列都必须有一个棋子,我们接下来考虑行的情况,列的情况同理。
注意到有这样一个事实:

  • 在每一行放置一个棋子的情况下,互相攻击的棋子必须在同一列,并且将\(n\)个棋子放入\(t\)列中,每一列至少有一个,总的攻击的对数为\(n-t\)

挺容易证明的,假设现在\(t\)列共有\(k\)对棋子互相攻击,那么有\(x_1+x_2+\cdots+x_t=k\),令\(y_i=x_i+1\),那么\(y_i\)即是第\(i\)列的棋子个数,所以就有\(y_1+y_2+\cdots+y_t=n\)。现在已知\(y_1+y_2+\cdots+y_t\),那么很容易推出\(x_1+x_2+\cdots+x_t\)

那么此时我们所求的方案数为\(\displaystyle {n\choose n-k}\cdot \begin{Bmatrix} n \\ n-k \end{Bmatrix}\cdot(n-k)!\)
后者为第二类斯特林数,因为我们这里不同的顺序会算上所有情况,那么最后还要乘以一个阶乘。
第二类斯特林数可以直接通过容斥来求,可以参见我之前写的博客
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/27 0:38:49
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << std::endl; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5, MOD = 998244353;

int qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}
ll n, k;
int fac[N], inv[N];

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

void run() {
    cin >> n >> k;
    if (k >= n) {
        cout << 0 << '\n';
        return;
    }
    fac[0] = 1;
    for (int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[N - 1] = qpow(fac[N - 1], MOD - 2);
    for (int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
    int ans = 0;
    for (int i = 0; i <= n - k; i++) {
        int res = 1ll * C(n - k, i) * qpow(n - k - i, n) % MOD;
        if(i & 1) res = MOD - res;
        ans = (ans + res) % MOD;
    }
    ans = 1ll * ans * C(n, n - k) % MOD;
    if(k) (ans <<= 1) %= MOD;
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}
posted @ 2020-04-27 19:11  heyuhhh  阅读(258)  评论(0编辑  收藏  举报