Codeforces Round 1098 (Div. 2)

A

贪心

有一个1,2,01,2,01,2,0组成的集合,每次选出一个子集,子集和必须被333整除,问最多能选多少个子集?

首先每个000都能单独作为一个合法子集,然后为例让组成的子集尽量多,要选性价比最高的选法,333个111或者333个222,才能组成一个被333整除,但是一个111一个222也行,所以先用一个111加一个222,等1/21/21/2其中某一种用完了,再把剩下的另一种每三个组成一个子集。

作为一个AAA题有点阴了。开始题也读错了,看成能组成多少个333,实际是能拆出多少个子集。。

c 复制代码
void solve() {
    int n;
    cin >> n;
    vector<int> c(3, 0); 
    for (int i = 0; i < n; i++) { 
        int x;
        cin >> x;
        c[x]++;
    }

    int ans = c[0];

    int mn = min(c[1], c[2]);
    ans += mn;

    c[1] -= mn;
    c[2] -= mn;

    if (c[1] > 0) {
        ans += c[1] / 3;
    }
    if (c[2] > 0) {
        ans += c[2] / 3;
    }

    cout << ans << '\n';
}

B

贪心 特判

一个环上,一个人跑,一个人追。每一回合都是跑的人先动,追的人后动。跑的人可以不动,也可以移动一格,最多kkk次,只能移动到相邻点。追的人可以不动,也可以移动一格,不限次数。

跑的人想让被追到的时间最晚,追的人想让追到的时间最早。问追到的时间最早多少?

显然追的人应该从环上较短的一边追,需要的时间就是较短的弧的长度,加上跑的人可以移动的kkk次。

比较阴的是,环长度不超过333的时候要特判,可能有很多移动次数kkk,但是游戏一回合就必定结束。

c 复制代码
void solve() {
	int n, x1, x2, k;
	cin >> n >> x1 >> x2 >> k;
	if (n <= 3) {
		cout << 1 << '\n';
		return;
	}

	int d = abs(x1 - x2);
	int ans = min(d, n - d);


	cout << ans + k << '\n';
}

C

数位贪心

给一个a<=1e18a<=1e18a<=1e18,给一个数位集合,是0−90-90−9的子集,问只使用集合内的数位,能组成的最接近aaa的数字bbb是多少

如果a,ba,ba,b的数位长度相同,考虑数位dpdpdp的思想,如果a,ba,ba,b再一个前缀上相同,接下来在下一位上,如果ai>bia_i>b_iai>bi,那么后面不论怎么选,都有b<ab\lt ab<a,那么想让bbb尽可能接近aaa,剩下的数位都要选集合内最大的数。

类似地,如果ai<bia_i<b_iai<bi,一定有b>ab\gt ab>a了,那么想让bbb尽可能接近aaa,剩下的数位都要选集合内最小的数。

如果ai=bia_i=b_iai=bi,转化成更小的子问题了,递归看下一位选什么。

按这个思路做一个搜索,就能得到一些候选元素,最接近的bbb就在这些元素里,对这些元素取最优即可。问题在于这样的搜索的复杂度是多少?注意到,如果某一位a,bia_,b_ia,bi不相等了,后面的数位可以全部直接确定,这个分支直接被剪掉了,只有ai=bia_i=b_iai=bi的分支可以一直延申。整个搜索树类似与一个竹竿,一个主干,主干上每个点额外长出两个叶子,整体的点数接近于O(3n)O(3n)O(3n),nnn是数位长度。

如果a,ba,ba,b的数位长度不同,那么可能的解只有两个情况,一是数位长度减一,全部由集合最大数组成,另一个是数位长度加一,全部由集合最小数组成(注意不能是000),把这两个元素也纳入考虑。

c 复制代码
string s;
vi d;
vector<i128> b;

void dfs(int i, int state, i128 cur) {
	if (i == s.size()) {
		b.push_back(cur);
		return;
	}

	if (state == 1) {
		dfs(i + 1, 1, cur * 10 + d.back());
	} else if (state == 2) {
		dfs(i + 1, 2, cur * 10 + d.front());
	} else {
		int y = s[i] - '0';
		for (int x : d) {
			if (x < y)dfs(i + 1, 1, cur * 10 + x);
			else if (x > y)dfs(i + 1, 2, cur * 10 + x);
			else dfs(i + 1, 0, cur * 10 + x);
		}
	}
}

void solve() {
	int a, n;
	cin >> a >> n;

	d.resize(n);
	for (int i = 0; i < n; ++i) cin >> d[i];

	s = to_string(a);
	int L = s.length();
	b.clear();

	dfs(0, 0, 0);

	if (L > 1) {
		i128 val = 0;
		for (int i = 0; i < L - 1; ++i) val = val * 10 + d.back();
		b.push_back(val);
	}

	int f = -1;
	for (int x : d) if (x > 0) {
			f = x;
			break;
		}
	if (f != -1) {
		i128 val = f;
		for (int i = 0; i < L; ++i) val = val * 10 + d.front();
		b.push_back(val);
	}

	i128 A = a;
	i128 mn = -1;
	for (i128 x : b) {
		i128 diff = (A > x) ? (A - x) : (x - A);
		if (mn == -1 || diff < mn) {
			mn = diff;
		}
	}

	int ans = mn;
	cout << ans << '\n';
}

D

扫描线 计数

平面上有一些整点,用两个直线y=k1+0.5,x=k2+0.5y=k_1+0.5,x=k_2+0.5y=k1+0.5,x=k2+0.5划分成四个区域,问有多少种不同的划分方案,满足每个区域至少有一个点。

考虑枚举一条线x=k1+0.5x=k_1+0.5x=k1+0.5,统计另一条线y=k2+0.5y=k_2+0.5y=k2+0.5的方案数。x=k1+0.5x=k_1+0.5x=k1+0.5的有效取值只有O(n)O(n)O(n)个,对所有出现过的xxx排序,两个xxx之间所有取值的答案都是一样的,只用枚举一次。

对于一个k1k_1k1,把点集划分成左右两半,假设是第iii个点,y+k2+0.5y+k_2+0.5y+k2+0.5的取值,需要保证四个区域至少都有一个点,所以为了保证左下,右下都有点,k2k_2k2要不小于左侧的最小yyy何和右侧的最小yyy,也就是k2>=max⁡(premini,sufmini+1)k_2>=\max(premin_i,sufmin_{i+1})k2>=max(premini,sufmini+1),同理,k2k_2k2要小于左侧的最大yyy和右侧的最大yyy,也就是k2<min⁡(premaxi,sufmaxi+1)k_2<\min(premax_i,sufmax_{i+1})k2<min(premaxi,sufmaxi+1)

这就是求在某一个区间内的不同yyy个数,由于点是静态的,把点的yyy拿出来排序,然后二分即可得到个数。

略卡常,除了排序之外都应该是线性的

c 复制代码
void solve() {
	int n;
	cin >> n;

	vector<pii> p(n + 1);
	vi Y;

	rep(i, 1, n) {
		cin >> p[i].fi >> p[i].se;
		Y.push_back(p[i].se);
	}

	sort(Y.begin(), Y.end());
	Y.erase(unique(Y.begin(), Y.end()), Y.end());

	vi mn(n + 1, n + 1), mx(n + 1, -1);

	rep(i, 1, n) {
		int x = p[i].fi, y = p[i].se;
		mn[x] = min(mn[x], y);
		mx[x] = max(mx[x], y);
	}

	vi premn, premx, sufmn, sufmx;
	rep(i, 1, n) {
		int v = mn[i];
		if (v != n + 1) {
			premn.push_back(v);
			sufmn.push_back(v);
		}
	}
	rep(i, 1, n) {
		int v = mx[i];
		if (v != -1) {
			premx.push_back(v);
			sufmx.push_back(v);
		}
	}


	int m = premn.size();
	if (m < 2) {
		cout << 0 << "\n";
		return;
	}
	rep(i, 1, m - 1) {
		premn[i] = min(premn[i], premn[i - 1]);
		premx[i] = max(premx[i], premx[i - 1]);
	}

	rep1(i, m - 2, 0) {
		sufmn[i] = min(sufmn[i], sufmn[i + 1]);
		sufmx[i] = max(sufmx[i], sufmx[i + 1]);
	}


	int ans = 0;

	rep(i, 0, m - 2) {
		int l = max(premn[i], sufmn[i + 1]);
		int r = min(premx[i], sufmx[i + 1]) - 1;

		if (l <= r) {
			auto it1 = lower_bound(Y.begin(), Y.end(), l);
			auto it2 = upper_bound(Y.begin(), Y.end(), r);
			ans += it2 - it1;
		}
	}

	cout << ans << "\n";
}
相关推荐
时空自由民.1 小时前
平衡车PID控制系统(豆包版本)
算法
sno_guo2 小时前
直播抠图技术100谈之25---调色中曲线是最优解
人工智能·算法·机器学习·直播·内容运营·obs抠图·直播技术
故事和你912 小时前
洛谷-【图论2-2】最短路1
开发语言·数据结构·c++·算法·动态规划·图论
Simple-Soft2 小时前
指针的高级应用与技巧 - C语言的灵魂
c语言·数据结构·算法
南宫萧幕2 小时前
Simulink 从零搭建 HEV ECMS 环境:模块解析、排坑指南与智能算法接口预留
人工智能·算法·matlab·汽车·控制
子豪-中国机器人2 小时前
词云与条形码答案
算法
闲人编程2 小时前
Agent的评估体系(AgentEval):如何判断一个Agent好坏?
大数据·人工智能·python·算法·agent·智能体·swe
沫璃染墨2 小时前
红黑树完全指南:从核心原理到插入验证全实现
开发语言·c++·算法
拉拉拉拉拉拉拉马2 小时前
Windsurf 最新版进阶讲解:从 Cascade 到 Devin Local,重新理解 AI 编程工作流
人工智能·算法