Oct. Training 1 - G

G. Hacker, pack your bags!

https://codeforces.com/problemset/problem/822/C

题意

给定n组数\(li, ri, ci\)代表一个区间范围和该区间的花费,给一个k值,求选择两个不重叠区间的区间长度和为k且花费和最小。

思路

将每个区间放进对应长度的vector里,然后对vector按区间左边界排序,从后遍历求后缀花费最小值,
再遍历每个区间为选择的第一个区间,长度为len,然后在k-len的vector中二分查找第二个区间,如果找得到,加上的花费就是恰好包含第二个区间的后缀最小值。每次答案取min。

#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const ll mod = 19650827;
const int N = 2e5 + 5;
const int maxn = 1e5 + 10;

ll n, m;
pair<pair<int, int>, int>p[N];
vector<pair<pair<int, int>, int>>ve[N];
map<pair<int, int>, int>mi;

void solve()
{
	cin >> n >> m;
	int l, r;
	ll v;
	for (int i = 1; i <= n; i++) {
		cin >> l >> r >> v;
		p[i] = { make_pair(l, r), v };
		ve[r - l + 1].push_back(p[i]);
	}
        //排序 求后缀最小值
	for (int i = 1; i <= 2e5; i++){
		sort(ve[i].begin(), ve[i].end());
		int mii = inf;
		for (int j = ve[i].size() - 1; j >= 0; j--) {
			mii = min(mii, ve[i][j].second);
			mi[{i, j}] = mii;
		}
    }

	int ans = 2e9 + 1;
	for (int i = 1; i <= n; i++) {
		int l = p[i].first.first;
		int r = p[i].first.second;
		int len = m - (r - l + 1);
		if (len <= 0 || !ve[len].size()) continue;
		int L = 0, R = ve[len].size() - 1, pos = -1;
                //二分找第一个不重叠的区间
		while (L <= R) {
			int mid = (L + R) / 2;
			if (mid >= ve[len].size()) break;
			if (ve[len][mid].first.first > r) {
				pos = mid;
				R = mid - 1;
			}
			else L = mid + 1;
		}
		//int pos = upper_bound(ve[len].begin(), ve[len].end(), r) - ve[len].begin();
		if (pos < 0) continue;
		ans = min(ans, p[i].second + mi[{len, pos}]);
	}
	if (ans == 2e9+1) cout << -1 << "\n";
	else cout << ans << "\n";
}

signed main()
{
	IOS;
	int _t = 1;
	//cin >> _t;
	while (_t--)
		solve();
	return 0;
}

F - Balance the Bits

https://codeforces.com/problemset/problem/1504/C

题意

给出一个01串 0代表可变 1代表不可变,问能否构造出一个包含'('和')'的字符串,在0位置的字符改变前后都满足括号匹配原则。

思路

容易想到的一点是左右两端必须是1 否则无解,且左右两端分别是'('和')'。
如果是奇数个1,也无法构造出来。
我们先不看1,就看0, 按'('、')'依次排列,即'()()()'的形式,然后将它们改变后就是')()()('的形式,因为左右两边一定由一个左括号和右括号,所以改变后也合法。
那么0的合法了,对于1,我们加上几对固定的'()'也一定合法,所以对于1,我们也只要按'('、')'依次构造即可。

#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const ll mod = 19650827;
const int N = 2e5 + 5;
const int maxn = 1e5 + 10;

ll n, m;
char a[N], b[N];
string s;

void solve()
{
	cin >> n;
	cin >> s;
	s = ' ' + s;
	ll sum = 0;
	a[1] = '(';
	a[n] = ')';
	int cnt0 = 0, cnt1 = 0;
	for (int i = 2; i < n; i++) {
		if (s[i] == '0') cnt0++;
		if (s[i] == '1') cnt1++;
		if (s[i] == '0' && cnt0 % 2) a[i] = '(';
		else if (s[i] == '1' && cnt1 % 2) a[i] = '(';
		else a[i] = ')';
	}

	if (s[1] == '0' || s[n] == '0' || cnt1 % 2) {
		cout << "NO\n";
		return;
	}

	for (int i = 1; i <= n; i++) {
		if (s[i] == '1') b[i] = a[i];
		else {
			b[i] = (a[i] == ')' ? '(' : ')');
		}
	}
	int x = 0;
	for (int i = 1; i <= n; i++) {
		if (b[i] == '(') x++;
		else x--;
		if (x < 0) {
			cout << "NO\n";
			return;
		}
	}
	if (x != 0) {
		cout << "NO\n";
		return;
	}
	cout << "YES\n";
	for (int i = 1; i <= n; i++) cout << a[i];
	cout << "\n";
	for (int i = 1; i <= n; i++) cout << b[i];
	cout << "\n";
}

signed main()
{
	IOS;
	int _t = 1;
	cin >> _t;
	while (_t--)
		solve();
	return 0;
}

E. Little Artem and Time Machine

https://codeforces.com/problemset/problem/669/E

题意

有三种操作:
1 \(t_i\) \(x_i\)\(t_i\)秒将\(x_i\)加入到集合中去
2 \(t_i\) \(x_i\)\(t_i\)秒将\(x_i\)从集合中删去(保证存在)
3 \(t_i\) \(x_i\) 询问第\(t_i\)秒集合中有几个\(x_i\)
顺序给出n次操作,后面操作对前面操作不影响,即便时间可能早于前面操作
对于每次操作3 输出一个值

思路

先将操作3都放入一个vector中,并按时间排序。
然后按操作循序一次遍历操作,如果某次操作时间晚于当前的操作3 就放入优先队列中(按时间从小到大),等到时间到了再弹出来操作,否则直接操作。
如果遍历到某个操作3 如果该操作3已经有答案就直接conitnue
不然就依次弹出队列中早于该操作的的操作,加给答案。如果当前操作3是当前按vector遍历到的vector,我们就向后遍历下一个操作3,否则将答案记录后将队列复原。

#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const ll mod = 19650827;
const int N = 2e5 + 5;
const int maxn = 1e5 + 10;

ll n, m;
pair<pair<ll, ll>, int>p[N];
map<ll, int>cnt;
int vis[N], ans[N];

void solve()
{
	cin >> n;
	int op;
	ll t, x;
	vector<pair<ll, int>>ve;
	for (int i = 1; i <= n; i++) {
		cin >> op >> t >> x;
		p[i] = { {t, x}, op };
		if (op == 3) ve.push_back({ t, i });
	}

	sort(ve.begin(), ve.end());

	priority_queue < pair<pair<ll, ll>, int>, vector<pair<pair<ll, ll>, int>> , greater<pair<pair<ll, ll>, int>> >q;
	int pre = 0;
	for (int i = 1; i <= n; i++) {
		while (vis[ve[pre].second]) pre++;
		ll now = ve[pre].first;
		while (!q.empty() && q.top().first.first < now) {
			int op = q.top().second;
			int x = q.top().first.second;
			op == 1 ? cnt[x]++ : cnt[x]--;
			q.pop();
		}

		ll t = p[i].first.first;
		ll x = p[i].first.second;
		ll op = p[i].second;

		if (op == 3) {
			int num = cnt[x];
			vector<pair<pair<ll, ll>, int > >res;
			while (!q.empty() && q.top().first.first < t) {
				int op = q.top().second;
				int xx = q.top().first.second;
				if(xx == x)
				    op == 1 ? num++ : num--;
				q.pop();
			}
                        //队列复原
			for (auto x : res) q.push(x);

			ans[i] = num;
			vis[i] = 1;
			continue;
		}

		if (t > now) {
			q.push(p[i]);
			continue;
		}
		op == 1 ? cnt[x]++ : cnt[x]--;
	}

	for (int i = 1; i <= n; i++) {
		if (p[i].second == 3) {
			cout << ans[i] << "\n";
		}
	}
}

signed main()
{
	IOS;
	int _t = 1;
	//cin >> _t;
	while (_t--)
		solve();
	return 0;
}
posted @ 2022-10-26 09:48  Yaqu  阅读(26)  评论(0)    收藏  举报