Educational Codeforces Round 121
[Link](Dashboard - Educational Codeforces Round 121 (Rated for Div. 2) - Codeforces)
A. Equidistant Letters (构造)
思路
因为每个字母最多有两个,让成对的字母距离相同,贪心来相同的都放在一起距离都为 1 1 1就可以了, s t r i n g string string自带字典序比较。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
string str; cin >> str;
sort(str.begin(), str.end());
cout << str << endl;
}
return 0;
}
B. Minor Reduction (贪心)
思路
贪心来看,能否使得加完位数不变,
如果可以的话我们尽量选择后面的(低位的)来操作,因为尽管位数不变但这个两位数一定边的更小了,我们尽量让它少乘 1 0 x 10^x 10x,这样损失更小。
否则位数一定变小的话我们让变小的靠前。
假设数为 a b c d e abcde abcde, 变最前为 ( a + b ) c d e (a+b)cde (a+b)cde,变23为 a ( b + c ) d e a(b+c)de a(b+c)de, ( a + b ) c d e > a ( b + c ) d e (a+b)cde > a(b+c)de (a+b)cde>a(b+c)de。 因为高位决定一切,既然位数相同一定越高位越大越好。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
bool check(char a, char b) {
int num = (a - '0' + b - '0');
return num >= 10;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
string str;
cin >> str;
bool ok = false;
int vis = -1;
for (int i = 0; i < str.size() - 1; i ++) {
if (check(str[i], str[i + 1])) {
ok = true;
vis = i;
}
}
if (ok) {
int a = str[vis] - '0', b = str[vis + 1] - '0';
a += b, b = a % 10, a /= 10;
str[vis] = (char)('0' + a), str[vis + 1] = (char)('0' + b);
for (int i = 0; i < str.size(); i ++)
cout << str[i];
cout << endl;
}
else {
int a = str[0] - '0', b = str[1] - '0';
a += b;
str[1] = (char)('0' + a);
for (int i = 1; i < str.size(); i ++)
cout << str[i];
cout << endl;
}
}
return 0;
}
C. Monsters And Spells (贪心+区间合并)
思路
想消耗越小我们应该尽可能的到达当前 h i h_i hi能放出恰好 k i k_i ki的技能伤害,但是如果我们在 h i − k i − 1 h_i-k_i-1 hi−ki−1放过技能就必须连续下来要不从 1 1 1开始放放到当前这个地技能伤害就不足了。
转化一下对于第 i i i个怪物我们需要 [ h i − k i + 1 , h i ] [h_i-k_i+1,h_i] [hi−ki+1,hi]都放技能,如果当前区间和前面某个区间重合,就必须连续放下来,所以等价于做个区间合并,对于所有的区间长度,分别求和公式求一下贡献即可。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<LL, LL> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int n, m;
LL k[N], h[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> k[i];
for (int i = 1; i <= n; i ++) cin >> h[i];
vector<PII> ve;
for (int i = 1; i <= n; i ++) ve.push_back({k[i] - h[i] + 1, k[i]});
sort(ve.begin(), ve.end());
LL st = -1, ed = -1;
LL res = 0;
for (int i = 0; i < ve.size(); i ++) {
if (st == -1) {
st = ve[i].x, ed = ve[i].y;
}
else if (ve[i].x <= ed) {
ed = max(ed, ve[i].y);
}
else {
res += (1 + ed - st + 1) * (ed - st + 1) / 2;
st = ve[i].x, ed = ve[i].y;
}
}
res += (1 + ed - st + 1) * (ed - st + 1) / 2;
cout << res << endl;
}
return 0;
}
D. Martial Arts Tournament(枚举+二分)
思路
我们将所有的体重排序,然后将相同体重的丢到一堆,就得到了 n n n个数表示从小到大每种体重有多少人。
对于怎么找到最优解首先想到对这 n n n个数 n + 1 n+1 n+1个空位选两个空位将其分成三份,每一份直接去找它的相邻的 2 k 2^k 2k算贡献,但是至少是 O ( n 2 ) O(n^2) O(n2)肯定不行。
换种方式枚举,枚举每一份最终要有的人是 2 2 2的几次幂,我们枚举第一份和第二份的幂次,然后对人数求个前缀和,对这个前缀和二分找到最靠近枚举 2 2 2的幂次的数,然后剩下的数做为第三份的人,再找一下第三份人对应的 2 2 2的幂次算下贡献即可。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m, k;
int a[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T --) {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
sort(a + 1, a + 1 + n);
vector<int> ve;
ve.push_back(0);
int cnt = 1;
for (int i = 2; i <= n; i ++)
if (a[i] != a[i - 1]) ve.push_back(cnt), cnt = 1;
else cnt ++;
ve.push_back(cnt);
n = ve.size() - 1;
for (int i = 1; i <= n; i ++) ve[i] += ve[i - 1];
int res = INF;
for (int i = 0; i < 20; i ++)
for (int j = 0; j < 20; j ++) {
int v1 = 1 << i, v2 = 1 << j;
int vis1, vis2, sum1, sum2, last;
vis1 = lower_bound(ve.begin(), ve.end(), v1) - ve.begin();
if (ve[vis1] == v1) sum1 = v1;
else sum1 = ve[vis1 - 1], vis1 --;
vis2 = lower_bound(ve.begin(), ve.end(), sum1 + v2) - ve.begin();
if (vis1 == vis2) sum2 = 0;
else if (sum1 + v2 == ve[vis2]) sum2 = v2;
else sum2 = ve[vis2 - 1] - sum1;
last = ve[n] - (sum1 + sum2);
for (int k = 0; k < 20; k ++)
if (1 << k >= last) {
last = (1 << k) - last;
break;
}
res = min(res, v1 - sum1 + v2 - sum2 + last);
}
cout << res << endl;
}
return 0;
}
E. Black and White Tree (换根dp)
思路
首先黑色的一定可以,和黑色相邻的点也一定可以。除去上述两种情况,对于任意一个点 u u u,如果 u u u可以走到黑色的格子等价于某个与它相邻的点 v v v可以走到黑色格子且以 v v v为根的子树至少存在两个黑色点。
设以 v v v为根的子树只有一个黑色点,因为 v v v可以走到黑色点且只有一个黑色点,所以 v v v与黑色点相邻,则会出现 u → v → 黑 色 点 u\to v\to 黑色点 u→v→黑色点,因为距离为 2 2 2所以无法走到。
设以 v v v为根的子树有两个黑色点 k 1 , k 2 k1,k2 k1,k2,因为 v v v可以到达某个黑色点,所以一定是沿着到 k 1 , k 2 k1,k2 k1,k2的路径交替进行的,不妨设先沿到 k 1 k_1 k1的路径,那么我们的 u u u就可以先沿着到 k 2 k_2 k2的路径走到 v v v,然后就与 v v v到黑色点的路径等价了。
因此我们做一个换根dp,维护一下以每个点为根的子树的黑点个数,按照三种成立情况判断即可。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 3e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N], res[N], cnt[N];
int sum;
void dfs_1(int u, int fa) {
if (a[u]) res[u] = true, cnt[u] = 1, sum ++;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue ;
dfs_1(j, u);
cnt[u] += cnt[j];
if (a[j] || res[j] && cnt[j] >= 2) res[u] = true;
}
}
void dfs_2(int u, int fa) {
if (a[fa] || res[fa] && sum - cnt[u] >= 2) res[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue ;
dfs_2(j, u);
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 0; i < n - 1; i ++) {
int a, b; cin >> a >> b;
add(a, b), add(b, a);
}
dfs_1(1, -1);
dfs_2(1, -1);
for (int i = 1; i <= n; i ++)
cout << res[i] << ' ';
cout << endl;
return 0;
}