Educational Codeforces Round 121

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 hiki1放过技能就必须连续下来要不从 1 1 1开始放放到当前这个地技能伤害就不足了。

​ 转化一下对于第 i i i个怪物我们需要 [ h i − k i + 1 , h i ] [h_i-k_i+1,h_i] [hiki+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 黑色点 uv,因为距离为 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值