Educational Codeforces Round 78 (Rated for Div. 2)

传送门

A. Shuffle Hashing

签到。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/12/21 10:03:44
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 100 + 5;
 
char s[N], t[N];
 
int pre[26];
int sum[N][26];
 
void run(){
    memset(pre, 0, sizeof(pre));
    cin >> (s + 1) >> (t + 1);
    int n = strlen(s + 1);
    for(int i = 1; i <= n; i++) ++pre[s[i] - 'a'];
    int m = strlen(t + 1);
    for(int i = 1; i <= m; i++) {
        for(int j = 0; j < 26; j++) sum[i][j] = sum[i - 1][j];
        ++sum[i][t[i] - 'a'];   
    }
    for(int i = 1; i <= m; i++) {
        for(int j = i; j <= m; j++) {
            static int cnt[26];
            for(int k = 0; k < 26; k++) cnt[k] = sum[j][k] - sum[i - 1][k];
            bool flag = false;
            for(int k = 0; k < 26; k++) {
                if(cnt[k] != pre[k]) flag = true;
            }       
            if(flag == false) {
                cout << "YES" << '\n';
                return;   
            }
        }
    }
    cout << "NO" << '\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. A and B

先全部加到较小的一方,然后考虑怎么取出一部分填另一方使得两者相等。

C. Berry Jam

题意:
现在有\(2n\)个糖果,每个糖果有红蓝两者颜色。
现在你位于中间的位置,左边\(n\)个糖果,右边\(n\)个糖果。每次只能吃连续的一段糖果。
现在回答最少吃多少个糖果,能使得剩下的糖果红蓝两种颜色的数量相等。

思路:

  • 考虑枚举一半,然后快速在另一半中找到答案。
  • 假设我们用\((x,y)\)表示红蓝两种颜色的数量,容易发现\(x,y\)的变化是连续的。
  • 假设我们目前枚举到了\(i\),并且将\(n+1\)~\(i\)的糖果都吃完,分别剩下\(r,b\)红、蓝糖果。
  • \(r>b\),那么就需要找到第一次\(d=r-b\)的位置,\(d=x-y\);否则,就需要找到第一次\(d=b-r\)的位置,\(d=y-x\)
  • 那么事先从\(n\)\(1\)扫一遍,存储一下第一次出现的位置即可。
  • 另外要记得排除一些特殊情况,比如只吃左半部分的糖果这种以及一个糖果都不吃。
  • 时间复杂度\(O(n)\)

代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/12/21 10:16:12
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5;
 
int n;
int a[N];
int x[N], y[N];
int q[N], top;
int q2[N], top2;
 
int find(int A, int B) {
    if(A == B) return n + 1;
    if(A > B) return q2[A - B];
    return q[B - A];
}
 
void run(){
    cin >> n;
    top = top2 = 0; 
    for(int i = 1; i <= 2 * n; i++) q[i] = q2[i] = 0;
    for(int i = 1; i <= 2 * n; i++) cin >> a[i];
    int r = 0, b = 0;
    for(int i = 1; i <= 2 * n; i++) {
        if(a[i] == 1) ++r; else ++b;
    }
    x[n + 1] = y[n + 1] = 0;
    for(int i = n; i >= 1; i--) {
        x[i] = x[i + 1]; y[i] = y[i + 1];   
        if(a[i] == 1) ++x[i]; else ++y[i];
        if(x[i] - y[i] > x[q2[top2]] - y[q2[top2]]) q2[++top2] = i;
        if(y[i] - x[i] > y[q[top]] - x[q[top]]) q[++top] = i;
    }
    int ans = 2 * n;
    int curr = 0, curb = 0;
    for(int i = n + 1; i <= 2 * n; i++) {
        if(a[i] == 1) ++curr;
        else ++curb;
        int nr = r - curr, nb = b - curb;
        int p = find(nr, nb);
        if(p) ans = min(ans, i - p + 1);
    }
    curr = curb = 0;
    for(int i = n; i >= 1; i--) {
        if(a[i] == 1) ++curr;
        else ++curb;
        if(r - curr == b - curb) {
            ans = min(ans, n - i + 1);   
        }
    }
    if(r == b) ans = 0;
    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;
}

D. Segment Tree

题意:
给出\(n\)个区间,每个区间坐标范围为\([1,2n]\),并且保证区间起点终点互不相等。
现在若存在两个区间相交(不包含、包含不算),那么对应的点将会有一条边。
现在判断最终\(n\)个点形成的图是否为一棵树。

思路:
对于树的判定,我们只要保证图连通且有\(n-1\)条边即可。
这一条件等价于存在\(n-1\)条边并且没有环。
那么我们用并查集维护点之间的关系,同时判断一下是否存在环即可。
一开始直接将当前区间和\(set\)中的元素进行合并,T了。之后发现其实左端点在当前区间左边的区间无用,所以直接二分到相交的区间合并即可。
因为至多会合并\(n\)次,所以复杂度为\(O(nlogn)\)

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/12/21 11:16:58
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;
 
int L[N], R[N];
struct seg {
    int l, r;   
    bool operator < (const seg &A) const {
        return l < A.l;
    }
}a[N];
int n, tot;
set <pii> s;
int f[N];
int find(int x) {return f[x] == x ? f[x] : f[x] = find(f[x]);}
int Union(int x, int l) {
    int res = 0;
    auto p = s.lower_bound(MP(l, 0));
    while(p != s.end()) {
        int y = p -> second;
        int fx = find(x), fy = find(y);
        if(fx == fy) return -1;
        f[fx] = fy;        
        if(++tot >= n) return -1;
        ++p;
    }
    return 1;
}
 
void run(){
    for(int i = 1; i <= 2 * n; i++) f[i] = i;
    for(int i = 1; i <= n; i++) cin >> a[i].l >> a[i].r;
    for(int i = 1; i <= n; i++) L[a[i].l] = R[a[i].r] = i;
    for(int i = 1; i < N; i++) {
        if(L[i]) s.insert(MP(a[L[i]].l, L[i]));
        if(R[i]) {
            int id = R[i];
            s.erase(MP(a[id].l, id));
            int t = Union(id, a[id].l);
            if(t == -1) {
                cout << "NO" << '\n';
                return;   
            }
        }
    }
    if(tot == n - 1) cout << "YES" << '\n';
    else cout << "NO" << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    while(cin >> n) run();
    return 0;
}

E. Tests for problem D

题意:
给出一棵树,要构造出对应的\(n\)个区间,使得满足D题的条件(就是给D造数据)。

思路:

  • 先确定儿子的左端点,然后即可确定当前结点的右端点。
  • 之后递归下去依次处理。

就没了...
但直接这样没能保证儿子之间互不相交。
观察到确定左端点时是从左到右逐个确定,那么确定右端点时我们从右往左逐个确定,就能使得儿子区间互相包含了。
细节详见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/12/22 16:21:48
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;
 
vector <int> G[N];
 
int n;
int L[N], R[N];
int tot;
 
void dfs(int u, int fa) {
    for(auto v : G[u]) if(v != fa) {
        L[v] = ++tot;
    }   
    R[u] = ++tot;
    for(int i = sz(G[u]) - 1; i >= 0; i--) {
        int v = G[u][i];
        if(v != fa) dfs(v, u);
    }
}
 
void run(){
    for(int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);   
    }
    L[1] = ++tot;
    dfs(1, 0);
    for(int i = 1; i <= n; i++) {
        cout << L[i] << ' ' << R[i] << '\n';   
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    while(cin >> n) run();
    return 0;
}

F. Cards

题意:

\[\sum_{i=0}^n{n\choose i}P^i(1-P)^{n-i}i^k \]

其中\(P=\frac{1}{m},n\leq 10^9,k\leq 5000\)

思路:
直接用第二类斯特拉数将幂次展开并化简:

\[\begin{aligned} &\sum_{i=0}^n{n\choose i}P^i(1-P)^{n-i}i^k\\ =&\sum_{i=0}^n{n\choose i}P^i(1-P)^{n-i}\sum_{j=0}^k{i\choose j}j!\begin{Bmatrix} k \\ j \end{Bmatrix}\\ =&\sum_{j=0}^k\begin{Bmatrix} k \\ j \end{Bmatrix}j!\sum_{i=0}^n{n\choose i}{i\choose j}P^i(1-P)^{n-i}\\ =&\sum_{j=0}^k\begin{Bmatrix} k \\ j \end{Bmatrix}j!{n\choose j}\sum_{i=0}^n{n-j\choose i-j}P^i(1-P)^{n-i}\\ =&\sum_{j=0}^k\begin{Bmatrix} k \\ j \end{Bmatrix}j!{n\choose j}P^j\sum_{i=0}^{n-j}{n-j\choose i}P^i(1-P)^{n-i-j}\\ =&\sum_{j=0}^k\begin{Bmatrix} k \\ j \end{Bmatrix}j!{n\choose j}P^j \end{aligned} \]

前面都是套路,最后两步较为巧妙,变换求和范围过后将乘积的形式化为了二项式定理展开的形式,然后直接将和式化简。
之后我们需要做的就是直接预处理第二类斯特林数即可。
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/12/21 12:40:57
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5005, MOD = 998244353;
 
int n, m, k;
int fac[N], S[N][N], C[N];
 
ll qpow(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 run(){
    S[0][0] = 1;
    for(int i = 1; i <= k; i++) {
        for(int j = 1; j <= i; j++) {
            S[i][j] = (1ll * S[i - 1][j] * j % MOD + S[i - 1][j - 1]) % MOD;
        }
    }
    C[0] = 1;
    for(int i = 1; i < N; i++) C[i] = 1ll * C[i - 1] * (n - i + 1) % MOD;
    int invm = qpow(m, MOD - 2);
    int ans = 0;
    for(int i = 0; i <= k; i++) {
        ans = (ans + 1ll * C[i] * S[k][i] % MOD * qpow(invm, i) % MOD) % MOD;
    }
    cout << ans << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    while(cin >> n >> m >> k) run();
    return 0;
}
posted @ 2019-12-22 17:02  heyuhhh  阅读(456)  评论(0编辑  收藏  举报