Educational Codeforces Round 74 (Rated for Div. 2)

传送门

A. Prime Subtraction

判断一下是否相差为\(1\)即可。

B. Kill 'Em All

随便搞搞。

C. Standard Free2play

题意:
现在有一个高度为\(h\)的悬崖,每一层有平台,但可能是隐藏状态。
高度为\(h\)的那层平台一定是在外面的,假设当前高度为\(x\),那么每次可以改变\(x\)\(x-1\)层平台的状态。
规定一个人若从\(x\)掉到\(x-1\)或者\(x-2\)都没事,否则就出事了。
问最少改变多少平台的状态,能够使在\(h\)高度的人顺利到达地面(高度为\(0\))。

思路:
我大概模拟了一下这个过程,然后发现对于连续的\(x,x-1,\cdots,x-k\),最终答案是否加一与\(x\)\(x-k\)的奇偶性相关。
并且从\(h_i\)\(h_{i+1}\),假设中间有空平台,其实最后到达的是\(h_{i+1}-1\),然后就这样模拟一下这个过程就行了。
注意一下细节:最后到地面的时候记得判断一下高度是否大于\(2\),否则答案会加一。

代码如下:

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
 
int q;
int h, n;
int a[N];
 
void run() {
	cin >> h >> n;
	a[n + 1] = 0;
	for(int i = 1; i <= n; i++) cin >> a[i];
	int ans = 0;
	for(int i = 1, j; i <= n; i = j + 1) {
		j = i;
		if(i != 1) {
			++i;
			if(a[i] != a[i - 1] - 1) {
				++ans;
				continue;
			}
		}
		if(i > n) break;
		j = i;
		while(j + 1 <= n && a[j] - a[j + 1] == 1) ++j;
		if((a[i] & 1) != (a[j] & 1) && a[j] != 1) ++ans;
	}
	cout << ans << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    cin >> q;
    while(q--) run();
    return 0;
}

D. AB-string

题意:
给出一个只含\(A,B\)的字符串,现在定义一个好的串是指:对于串中的每个数,都包含在一个长度大于\(1\)的回文串内。
问给出的字符串中,有多少好的串。

思路:

  • 容易发现,对于一个长度大于\(2\)串,其中间的数一定被包含在某个回文中;
  • 所以我们直接考虑两边的数。
  • 进一步发现,只有这样的串不满足条件:\(A\)或者\(B\)只出现一次,并且出现在两端某个位置。
  • 所以最终不合法的情况一定是“一段一段”的,可以直接压缩连续的段,统计个数然后直接算就行。

我做法是正反扫两边来搞的,稍微复杂一点。

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 3e5 + 5;
 
int n;
char s[N];
 
void run() {
    cin >> (s + 1);
    ll ans = 0;
    for(int i = 1, j; i < n; i = j) {
        j = i;
        while(j <= n && s[j] == s[i]) ++j;
        --j; i = j;
        while(j + 1 <= n && s[j + 1] != s[i]) ++j;
        ans += j - i;
    }
    for(int i = n, j; i > 1; i = j) {
        j = i;
        while(j >= 1 && s[j] == s[i]) --j;
        ++j; i = j;
        while(j - 1 >= 1 && s[j - 1] != s[i]) --j;
        if(i - j - 1 >= 0) ans += i - j - 1;
    }
    ans = 1ll * (n - 1) * n / 2 - ans;
    cout << ans << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(cin >> n) run();
    return 0;
}

E. Keyboard Purchase

题意:
给出一个最多由前\(m\)个字母组成的字符串。
现在串的贡献就为\(\sum_{i=2}^{n}|pos_{s_{i-1}}-pos_{s_i}|\)\(pos_c\)表示\(c\)这个字符在排列中的位置,这个排列是自己定的。
现在就要求所有排列中最小的贡献。

思路:

  • 一开始想的就是枚举第一个位置的数...枚举第二个位置的数...但复杂度是阶乘级别的,然后没做出来;然后将问题转化为二元组,似乎也不行...就卡住了。
  • 后来发现,直接可以二进制压缩,当二进制上面有\(x\)\(1\)时,就相当于固定了前\(x\)个位置,位置固定后,绝对值就很好化简了。
  • 然后枚举新添加进来的数,把绝对值拆开单独计算它的贡献,比如它和前面某些数相邻,此时他的贡献就是正的,对于其它的,他的贡献就是负的。
  • 直接做复杂度是\(O(2^m*m^2)\)的,可以预处理一下\(g(state,i)\)表示\(state\)中与\(i\)相邻的有多少个,转移直接利用二进制最后一位来进行转移,显然统计出来的\(g(state,i)\)不重不漏,最终复杂度就为\(O(2^m*m)了\)

关键在于状态的定义,除开一般的“存在性”的含义,还有隐性含义固定前面的位置,并且仔细思考,这样其实可以直接枚举到所有的情况,之前似乎还没碰过这种hhh,感觉很巧妙。
详见代码:

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e6 + 5, M = 20;
 
int n, m;
char s[N];
int cnt[M][M], num[N];
 
int g[N][M], f[N];
int lg2[N];
 
int lowbit(int x) {return x & -x;}
 
void run() {
    cin >> (s + 1);
    memset(cnt, 0, sizeof(cnt));
    memset(f, INF, sizeof(f));
    memset(num, 0, sizeof(num));
    int lim = 1 << m;
    for(int i = 2; i < 1 << m; i++) lg2[i] = lg2[i >> 1] + 1;
    for(int i = 1; i < n; i++) {
        ++cnt[s[i] - 'a'][s[i + 1] - 'a'];
        ++cnt[s[i + 1] - 'a'][s[i] - 'a'];
    }
    for(int i = 0; i < lim; i++) {
        for(int j = 0; j < m; j++) {
            if(i == 0) g[i][j] = 0;
            else g[i][j] = g[i ^ lowbit(i)][j] + cnt[j][lg2[lowbit(i)]];
        }
    }
    for(int i = 0; i < lim; i++) {
        for(int j = 0; j < m; j++) {
            if(i >> j & 1) ++num[i];
        }
    }
    f[0] = 0;
    for(int i = 0; i < lim; i++) {
        for(int j = 0; j < m; j++) {
            if(i >> j & 1) {
                int msk1 = i ^ (1 << j);
                int msk2 = (lim - 1) ^ i;
                f[i] = min(f[i], f[msk1] + num[i] * (g[msk1][j] - g[msk2][j]));
            }
        }
    }
    cout << f[lim - 1] << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(cin >> n >> m) run();
    return 0;
}

F. The Maximum Subtree

题意:
求树上最大毛毛虫。

思路:
直接\(dp\)就行,一开始初始化错了改了一小时...
感觉难点就是问题的转化?但感觉问题转化也不是很难...

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 3e5 + 5;
 
int n, q;
int ans;
int g[N];
struct Edge {
    int v, next;
}e[N << 1];
 
int head[N], tot;
void adde(int u, int v) {
    e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
}
 
void dfs(int u, int fa) {
    int son = 0, mx = 0, mx2 = 0;
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if(v == fa) continue;
        ++son;
        dfs(v, u);
        g[u] = max(g[u], g[v]);
        if(g[v] > mx) {
            mx2 = mx, mx = g[v];
        } else if(g[v] > mx2) mx2 = g[v];
    }
    if(son == 0) {
        g[u] = 1; return;
    }
    ans = max(ans, mx + mx2 + max(son - 1, 1) + (fa != 0));
    g[u] += son;
}
 
void run() {
    cin >> n;
    for(int i = 1; i <= n; i++) head[i] = -1, g[i] = 0;
    tot = 0;
    for(int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        adde(u, v); adde(v, u);
    }
    ans = 0;
    dfs(1, 0);
    cout << ans << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    cin >> q;
    while(q--) run();
    return 0;
}
posted @ 2019-10-09 22:17  heyuhhh  阅读(471)  评论(0编辑  收藏  举报