VP Educational Codeforces Round 159 (Rated for Div. 2)

A. Binary Imbalance

题意:给你一个01串,每次选一个01或者一个10在他们中间插一个0进去,问能不能让0的个数大于1。

我们进行一次插入操作后,显然还可以继续操作,所以只要有0和1就一定可以。注意特判全0的情况。

点击查看代码
void solve() {
   	int n;
   	std::cin >> n;
   	std::string s;
   	std::cin >> s;
   	if (s.find('1') == s.npos || s.find('0') != s.npos) {
   		std::cout << "YES\n";
   	} else {
   		std::cout << "NO\n";
   	}
}

B. Getting Points

题意:一共n天,你要在n天获得m分,让自己不被开除。每过七天会增加一个任务,每天可以上课,上课可以加x分,做任务可以加y分,每天只能上一次课,一次课可以最多做两个任务,如果每天都去上课也可以不被开除。但你想让自己休息的天数最多。

先计算有多少任务可以做,那么优先上课做两个任务,这样可以得 cnt / 2 * (x + 2 * y)分,如果m小于等于这个就直接输出 max(0, n - mx+2y),否则先看剩下还有没有一个单独的任务,做完所有任务后判断还需要上多少天课就行。

点击查看代码
void solve() {
 	i64 n, m, x, y;
 	std::cin >> n >> m >> x >> y;
 	i64 cnt = (n + 6) / 7;
 	if (cnt / 2 * (x + 2 * y) < m) {
 		m -= cnt / 2 * (x + 2 * y);
 		i64 ans = cnt / 2;
 		if (cnt & 1) {
 			m -= x + y;
 			++ ans;
 		}

 		ans += std::max(0ll, (m + x - 1) / x);
 		std::cout << std::max(0ll, n - ans) << "\n";
 	} else {
 		std::cout << std::max(0ll, n - (m + x + 2 * y - 1) / (x + 2 * y)) << "\n";
 	}
}

C. Insert and Equalize

题意:给你一个数组,你要加一个不在这个数组里的数an+1进去,然后选一个x(x > 0),然后每个数不断加x到每个数相等。问最少加多少次。

对a排序后,x就是所有 ai - ai1的最大公约数,因为它要满足能从an1变到an那么x是an1-an的因子,an2变到an的过程中肯定要经过an1, 所以x是an1-an2的因子,同理,他应该是所有 ai - ai1的最大公约数。an+1选一个最大的不在a里的x的倍数就行。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<i64> a(n);
    i64 max = -2e9;
    std::set<i64> s;
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    	s.insert(a[i]);
    	max = std::max(max, a[i]);
    }

    if (n == 1) {
    	std::cout << 1 << "\n";
    	return;
    }

    std::sort(a.begin(), a.end());
    i64 d = 0;
    for (int i = 1; i < n; ++ i) {
    	d = std::gcd(d, a[i] - a[i - 1]);
    }

    d = std::abs(d);
    i64 an = max - d;
    for (i64 i = max - d; ; i -= d) {
    	if (!s.count(i)) {
    		an = i;
    		break;
    	}
    }
 
    i64 ans = (max - an) / d;
    for (int i = 0; i < n; ++ i) {
    	ans += (max - a[i]) / d;
    }

    std::cout << ans << "\n";
}

D. Robot Queries

题意:从(0, 0)开始移动,给你一个移动序列,然后q次询问,每次问你如果翻转[l, r]区间的操作,能不能经过(x, y)。

这题主要看你能不能看出翻转后坐标和翻转前坐标的关系。
注意到[1, l - 1]不变,然后因为x和y总移动的值不变,所以[r + 1, n]坐标不变。
那么对于[l, r]之间的坐标是怎么变化的? 画图发现应该是围着中心点旋转了180度。但具体坐标是多少?记pi是不翻转执行i次操作后的下标,dij表示从i到j操作对xy的影响, 那么dij = pj - pi1。那么如果(x, y)在翻转[l, r]这个区间后出现过,意味着有一个i满足(pxl1 + dxir, pyl1 + dyir) = (x, y), 那么就是(pxl1 + pxr - pxi1pyl1 + pyr - pyi1) = (x, y), 得到 pxi1 = pxl1 + pxr - x, pyi1 = pyl1 + pyr - y,也就是说,我们只要看没翻转之前[l - 1, r - 1]有没有(pxl1 + pxr - x, pyl1 + pyr - y)这个坐标就行了。
我是把询问换成三部分,问[0, l - 1]有没有出现(x, y), [r, n]有没有出现(x, y), [l - 1, r - 1]有没有出现(pxl1 + pxr - x, pyl1 + pyr - y)。
离线回答就行,用个map存每个坐标最后出现的地方。

点击查看代码
void solve() {
    int n, q;
    std::cin >> n >> q;
    std::string s;
    std::cin >> s;
    std::vector<std::pair<int, int> > path(n + 1);
    for (int i = 0; i < n; ++ i) {
    	path[i + 1] = path[i];
    	if (s[i] == 'U') {
    		++ path[i + 1].second;
    	} else if (s[i] == 'D') {
    		-- path[i + 1].second;
    	} else if (s[i] == 'L') {
    		-- path[i + 1].first;
    	} else {
    		++ path[i + 1].first;
    	}
    }

    std::vector<std::vector<std::array<int, 4> > > Q(n + 1);
    for (int i = 0; i < q; ++ i) {
    	int x, y, l, r;
    	std::cin >> x >> y >> l >> r;
    	Q[l - 1].push_back({x, y, 0, i});
    	Q[n].push_back({x, y, r, i});
    	Q[r - 1].push_back({path[l - 1].first + path[r].first - x, path[l - 1].second + path[r].second - y, l - 1, i});
    }

    std::map<std::pair<int, int> , int> idx;
    std::vector<int> ans(q);
    for (int i = 0; i <= n; ++ i) {
    	idx[path[i]] = i;
    	for (auto & [x, y, l, id] : Q[i]) {
    		if (idx.count({x, y}) && idx[{x, y}] >= l) {
    			ans[id] = 1;
    		}
    	}
    }

    for (int i = 0; i < q; ++ i) {
    	if (ans[i]) {
    		std::cout << "YES\n";
    	} else {
    		std::cout << "NO\n";
    	}
    }
}

E. Collapsing Strings

题意:两个字符串的C(x, y)为字符串x翻转后和y的最长公共前缀长度(lcp)。给你n个字符串求i=1ni=1nC(si, sj)。

首先C(si, sj) = |si| + |sj| - 2 × lcp(sj, siR)。 siRsi翻转后的字符串。
我们对每个字符串单独考虑,只需要考虑每个字符串在前面或者后面的情况,不然就会算重。假设考虑字符串i在后面,那么i的贡献为j=1n C(sj, si)
= j=1n |si| + |sj| - 2 × lcp(si, sjR)
= |si| × n + j=1n|sj| - 2 × j=1nlcp(si, sjR)
那么前两个都是有的东西,我们只需要算2 × j=1nlcp(si, sjR)。
那么我们把每个siR都插到字典树里取,那么就可以查询所有串的反串和si的公共前缀总和。

点击查看代码
struct Trie {
	std::vector<std::array<int, 26> > tr;
	std::vector<int> cnt;
	int idx;
	Trie() {
		tr.push_back({});
		cnt.push_back(0);
		idx = 0;
	}

	void insert(std::string s) {
		int p = 0;
		for (auto & c : s) {
			int x = c - 'a';
			if (!tr[p][x]) {
				tr[p][x] = ++ idx;
				tr.push_back({});
				cnt.push_back(0);
			}

			p = tr[p][x];
			cnt[p] += 1;
		}
	}

	i64 query(std::string s) {
		int p = 0;
		i64 res = 0;
		for (auto & c : s) {
			int x = c - 'a';
			if (!tr[p][x]) {
				return res;
			}

			p = tr[p][x];
			res += cnt[p];
		}

		return res;
	}
};

void solve() {
    int n;
    std::cin >> n;
    std::vector<std::string> a(n);
    Trie tr;
    i64 sum = 0;
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    	std::string t = a[i];
    	std::reverse(t.begin(), t.end());
    	tr.insert(t);
    	sum += a[i].size();
    }	

    i64 ans = 0;
    for (int i = 0; i < n; ++ i) {
    	ans += n * a[i].size() + sum - tr.query(a[i]) * 2;
    }
    std::cout << ans << "\n";
}
posted @   maburb  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示