2022ICPC西安 - C E F G J L

The 2022 ICPC Asia Xian Regional Contest

C 思维
E 思维
F 思维
G 字符串思维
J 签到
L 图论

C. Clone Ranran

策略:先整体复制自己,再一起出题。
枚举复制自己的次数,所有取最小花费时间

void solve(){
	ll a, b, c, ans = INF;
	cin >> a >> b >> c;
	int i, cnt;
	for(i = 1, cnt = 0; i <= c; i *= 2, ++ cnt){
		ans = min(ans, b * (c / i + (c % i != 0)) + a * cnt);
	}
	ans = min(ans, b * (c / i + (c % i != 0)) + a * cnt);
	cout << ans << '\n';
	return ;
}

E. Find Maximum

题目与三进制有关
函数 f(x) 的意义是 x 的三进制表示各数位上的数字之和加上三进制的长度
那么尽可能构造0222222...等三进制数即可
可以从前往后找三进制表示最高位出现不同的一位,再在这个位开始后面尽可能多的放 2,并且使得总和不变时数位尽可能多

string gets(ll x){
	string a = "";
	while(x){
		a = char(x % 3 + '0') + a;
		x /= 3;
	}
	return a;
}

void solve(){
	ll l, r;
	cin >> l >> r;
	string a, b;
	a = gets(l);
	b = gets(r);
	ll ans = 0;
	if(b.size() > a.size()){
		ans += 2 * (b.size() - 1) + b[0] - '0';
		bool f = true;
		for(int i = 1; i < b.size(); ++ i){
			if(b[i] != '2'){
				f = false; break;
			}
		}
		if(!f){
			-- ans;
			ans += b.size();
			if(b[0] == '1') -- ans;
		}else ans += b.size();
		if(b[0] == '1' && !f && b.size() > 1){
			bool up = false;
			if(b[1] == '2') up = true;
			else if(b[1] == '1'){
				up = true;
				for(int i = 2; i < b.size(); ++ i){
					if(b[i] != '2'){
						up = false; break;
					}
				}
			}
			if(up) ++ ans;
		}
	}else{
		int n = a.size();
		for(int i = 0; i < n; ++ i){
			if(b[i] > a[i]){
				ans += 2 * (n - 1 - i) + b[i] - '0';
				bool f = true;
				for(int j = i + 1; j < n; ++ j){
					if(b[j] != '2'){
						f = false; break;
					}
				}
				if(!f) -- ans;
				break;
			}else ans += b[i] - '0';
		}
		ans += b.size();
	}
	cout << ans << '\n';
	return ;
}

或者,直接从 r 出发,枚举中转点,其前与 r 一样,其后全是2
算一次 f 最多 30 次,枚举最多 30 * 30 次

ll f(ll x){
	if(x == 0) return 1;
	else if(x % 3) return f(x - x % 3) + x % 3;
	else return f(x / 3) + 1;
}

void solve(){
	ll l, r;
	cin >> l >> r;
	vector<int> a;
	ll t = r, ans = f(r);
	while(t){
		a.push_back(t % 3);
		t /= 3;
	}
	reverse(a.begin(), a.end());
	for(int i = 0; i < a.size(); ++ i){
		if(a[i] == 0) continue;
		ll p = 0;
		for(int j = 0; j < i; ++ j){
			p = p * 3 + a[j];
		}
		p = p * 3 + a[i] - 1;
		for(int j = i + 1; j < a.size(); ++ j){
			p = p * 3 + 2;
		}
		if(p >= l) ans = max(ans, f(p));
	}
	cout << ans << '\n';
	return ;
}

F. Hotel

注意仔细看题!!!
注意到住2人间只能同一队同性别住,所以对于每一个队伍单独考虑即可,均住单住单人房、均住单住双人房、一单一双,两双,花费取小统计即可

void solve(){
	int n, c1, c2, ans = 0;
	cin >> n >> c1 >> c2;
	for(int i = 0; i < n; ++ i){
		string ss;
		cin >> ss;
		int cost = min(c1 * 3, c2 * 3);
		if(ss[0] == ss[1] || ss[1] == ss[2] || ss[0] == ss[2]){
			cost = min(cost, c1 + c2);
			cost = min(cost, c2 * 2);
		}
		ans += cost;
	}
	cout << ans << '\n';
	return ;
}

G. Perfect Word

长度为1的字符串一定是完美字符串
如果说一个长度大于2的字符串,仅去掉首元素和仅去掉末尾元素的两个字符串均出现在给定的字符串中,那么其也是一个完美字符串
那么对于原字符串按照长度从小到大排序,从前往后判断并记录下所有的完美字符串,同时更新答案即可

void solve(){
	int n;
	cin >> n;
	string ss[n];
	for(int i = 0; i < n; ++ i){
		cin >> ss[i];
	}
	sort(ss, ss + n, [&](string x, string y){
		return x.size() < y.size();
	});
	int ans = 0;
	map<string, int> q;
	for(int i = 0; i < n; ++ i){
		if(ss[i].size() == 1){
			q[ss[i]] = 1;
			ans = max(ans, 1);
			continue;
		}
		string a = ss[i].substr(1), b = ss[i].substr(0, ss[i].size() - 1);
		if(q[a] == (int)a.size() && q[b] == (int)b.size()){
			q[ss[i]] = ss[i].size();
			ans = max(ans, (int)ss[i].size());
		}
	}
	cout << ans << '\n';
	return ;
}

J. Strange Sum

如果说我们依次选了三个,那么下标最大的那一个一定不能选,因为它及之间已经选择了三个元素,不符合题意
所以最终,最多选择两个最大的正数,没有正数可以不选

void solve(){
	int n;
	cin >> n;
	vector<int> a(n);
	for(int i = 0; i < n; ++ i) cin >> a[i];
	sort(a.begin(), a.end());
	ll ans = 0;
	for(int i = n - 1; i > n - 3; -- i)
		if(a[i] > 0) ans += a[i];
	cout << ans << '\n';
	return ;
}

L. Tree

可以发现,符合条件一,即为一条链;符合条件二,即为反链

要使最后的子点集最少,那么反链一定是由叶子节点逐层向上取得的,链就不必说了
那么枚举反链的个数,反链的个数 + 剩下链的个数就是此时的子点集数,过程中的最小即为答案

\(tag[i]\) 维护从下往上长为 i 的关键链的个数,关键链意味如果从叶子节点开始删点 i 次,那么 \(tag[i]\) 条链都将被移除

初始 ans 为链的总数,最大的链长度即为可以分为最多的符合条件二的集合的个数

枚举从叶子节点开始删点的次数,删点的次数加上剩下链的个数即为分成的子点集的个数,过程中取最小即可

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int N = 1e6 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;

int dfs(vector<int> *e, vector<int> & tag, int u, int fa){
	int len = 1;
	vector<int> l;
	for(auto v : e[u]){
		if(v == fa) continue;
		int t = dfs(e, tag, v, u);
		l.push_back(t);
	}
	if(l.size()){
		sort(l.begin(), l.end());
		int size = l.size();
		for(int i = 0; i < size - 1; ++ i)
			++ tag[l[i]];
		len += l.back();
	}
	return len;
}

void solve(){
	int n;
	cin >> n;
	vector<int> tag(n + 1, 0), e[n + 1];
	for(int i = 2; i <= n; ++ i){
		int v;
		cin >> v;
		e[i].push_back(v);
		e[v].push_back(i);
	}
	int mlen = dfs(e, tag, 1, 0);
	++ tag[mlen];
	int ans = accumulate(tag.begin(), tag.end(), 0);
	int now = 0, res = ans;
	for(int i = 0; i <= mlen; ++ i){
		now += tag[i];
		ans = min(ans, res + i - now);
	}
	cout << ans << '\n';
	return ;
}

signed main(){
	// freopen("in.txt", "r", stdin);
	// freopen("out.txt", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}
posted on 2023-11-28 19:06  Qiansui  阅读(12)  评论(0编辑  收藏  举报