AtCoder Regular Contest 114 F Permutation Division

Posted zltzlt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Regular Contest 114 F Permutation Division相关的知识,希望对你有一定的参考价值。

洛谷传送门

AtCoder 传送门

这题居然是之前某场模拟赛(contest 701)的 T1……(@Vidoliga 场切但是被卡常/bx)

下面记 \\(m\\) 为原题面中的 \\(K\\)\\(a_i\\) 为原题面中的 \\(P_i\\)

不难发现后手的策略是把所有段按照段的第一个数从大到小排序接在一起。

考虑若 \\(a_1 \\le m\\),则先手能把所有 \\(\\le m\\) 的数都作为段头。

\\(a_1 > m\\),操作后的排列字典序一定大于等于原排列(后手可以选择不动)。于是先手想让与原序列的 \\(\\textLCP\\) 尽量大。

预处理出以 \\(a_1\\) 开头,\\(a_i\\) 结尾的 \\(\\textLDS\\),记为 \\(f_i\\)。考虑枚举 \\(i\\),那么 \\(i\\)\\(i\\) 前面有 \\(f_i\\) 个元素可以作为段的开头,设它们为 \\(1 = p_1 < p_2 < \\cdots < p_f_i = i\\),则后面还需要 \\(m - f_i\\) 段。找到后面第一个位置 \\(k\\) 使得 \\(\\sum\\limits_x=k+1^n [a_x < a_i] = m - f_i\\),那么 \\(k\\) 就是前面分成 \\(f_i\\)\\(p_1 \\sim p_f_i\\) 这种方案的 \\(\\textLCP\\)。注意如果 \\(\\textLCP\\) 相同,则先手想让 \\(k + 1 \\sim n\\) 还需要分的段尽量少(即 \\(m - f_i\\) 尽量小)。

\\(k\\) 可以使用主席树+二分,因此时间是 \\(O(n \\log^2 n)\\) 的。

code
// Problem: F - Permutation Division
// Contest: AtCoder - AtCoder Regular Contest 114
// URL: https://atcoder.jp/contests/arc114/tasks/arc114_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 200100;

int n, m, a[maxn], f[maxn];

inline bool cmp(const pii &x, const pii &y) 
	return a[x.fst] > a[y.fst];


namespace BIT 
	int c[maxn];
	
	inline void update(int x, int d) 
		for (int i = x; i; i -= (i & (-i))) 
			c[i] = max(c[i], d);
		
	
	
	inline int query(int x) 
		int res = 0;
		for (int i = x; i <= n; i += (i & (-i))) 
			res = max(res, c[i]);
		
		return res;
	


int rt[maxn], ls[maxn << 6], rs[maxn << 6], tree[maxn << 6], ntot;

int build(int l, int r) 
	int rt = ++ntot;
	if (l == r) 
		return rt;
	
	int mid = (l + r) >> 1;
	ls[rt] = build(l, mid);
	rs[rt] = build(mid + 1, r);
	return rt;


int update(int rt, int l, int r, int x) 
	int u = ++ntot;
	ls[u] = ls[rt];
	rs[u] = rs[rt];
	tree[u] = tree[rt] + 1;
	if (l == r) 
		return u;
	
	int mid = (l + r) >> 1;
	if (x <= mid) 
		ls[u] = update(ls[u], l, mid, x);
	 else 
		rs[u] = update(rs[u], mid + 1, r, x);
	
	return u;


int query(int rt, int l, int r, int ql, int qr) 
	if (ql > qr) 
		return 0;
	
	if (ql <= l && r <= qr) 
		return tree[rt];
	
	int mid = (l + r) >> 1, res = 0;
	if (ql <= mid) 
		res += query(ls[rt], l, mid, ql, qr);
	
	if (qr > mid) 
		res += query(rs[rt], mid + 1, r, ql, qr);
	
	return res;


void solve() 
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) 
		scanf("%d", &a[i]);
	
	if (a[1] <= m) 
		vector<pii> vc;
		for (int i = 1, j = 1; i <= n; i = (++j)) 
			while (j < n && a[j + 1] > m) 
				++j;
			
			vc.pb(i, j);
		
		sort(vc.begin(), vc.end(), cmp);
		for (pii p : vc) 
			for (int i = p.fst; i <= p.scd; ++i) 
				printf("%d ", a[i]);
			
		
		return;
	
	rt[n + 1] = build(1, n);
	for (int i = n; i; --i) 
		rt[i] = update(rt[i + 1], 1, n, a[i]);
	
	f[1] = 1;
	BIT::update(a[1], 1);
	for (int i = 2; i <= n; ++i) 
		if (a[i] > a[1]) 
			continue;
		
		f[i] = BIT::query(a[i] + 1) + 1;
		BIT::update(a[i], f[i]);
	
	int lcp = 0, num = 0;
	for (int i = 1; i < n; ++i) 
		if (a[i] > a[1] || query(rt[i + 1], 1, n, 1, a[i] - 1) < m - f[i]) 
			continue;
		
		int l = i, r = n - 1, pos = -1;
		while (l <= r) 
			int mid = (l + r) >> 1;
			if (query(rt[mid + 1], 1, n, 1, a[i] - 1) >= m - f[i]) 
				pos = mid;
				l = mid + 1;
			 else 
				r = mid - 1;
			
		
		if (pos != -1 && (pos > lcp || (pos == lcp && f[i] > f[num]))) 
			lcp = pos;
			num = i;
		
	
	for (int i = 1; i <= lcp; ++i) 
		printf("%d ", a[i]);
	
	vector<pii> vc;
	for (int i = lcp + 1, j = lcp + 1; i <= n; i = (++j)) 
		while (j < n && a[j + 1] > a[num]) 
			++j;
		
		vc.pb(i, j);
	
	sort(vc.begin(), vc.end(), cmp);
	for (pii p : vc) 
		for (int i = p.fst; i <= p.scd; ++i) 
			printf("%d ", a[i]);
		
	


int main() 
	int T = 1;
	// scanf("%d", &T);
	while (T--) 
		solve();
	
	return 0;


AtCoder Regular Contest 098

传送门

C - Attention

前后缀搞一搞即可。


Code

#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 3e5 + 5;

int n;
char s[N];
int pre[N], suf[N];

void run() {
    cin >> s + 1;
    for(int i = 1; i <= n; i++) {
        pre[i] = pre[i - 1];
        if(s[i] == 'W') ++pre[i];
    }
    suf[n + 1] = 0;
    for(int i = n; i >= 1; i--) {
        suf[i] = suf[i + 1];
        if(s[i] == 'E') ++suf[i];
    }
    int ans = n + 1;
    for(int i = 1; i <= n; i++) {
        ans = min(ans, pre[i - 1] + suf[i + 1]);
    }
    cout << ans << '
';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(cin >> n) run();
    return 0;
}

D - Xor Sum 2

题意:
给出一个长度为(n)的序列(a),现在问有多少区间([l,r]),满足:
[ a_l xor a_{l+1} xor cdots xor x_r=a_l+a_{l+1}+cdots+a_r ]

思路:
注意这样一个性质:(a_i xor a_jleq a_i+a_j)
所以一旦一个区间不满足条件过后,更大的区间也不满足了;一个区间满足条件,更小的区间也满足。
所以直接双指针搞搞就行。


Code

#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5;

int n;
int a[N];

void run() {
    for(int i = 1; i <= n; i++) cin >> a[i];
    int j = 0;
    ll sum1 = 0, sum2 = 0;
    ll ans = 0;
    for(int i = 1; i <= n; i++) {
        while(j + 1 <= n) {
            if(sum1 + a[j + 1] == (sum2 ^ a[j + 1])) {
                sum1 += a[++j], sum2 ^= a[j];
            }
            else break;
        }
        ans += j - i + 1;
        sum1 -= a[i]; sum2 ^= a[i];
    }
    cout << ans << '
';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(cin >> n) run();
    return 0;
}

E - Range Minimum Queries

题意:
给出一个长度为(n)的序列(a),现在执行(q)次操作,每次选择一段长度为(k)的区间,删除一个最小的数(如果有多个,可任意删除一个)。
最后问在所有删除的数中,相差最小为多少。

思路:

  • 很容易想到二分差值,然后枚举下界,这样就知道了上下界。
  • 因为每次必须选择最小的一个数,所以所有大于等于下界的数构成了一个个可执行区间(因为若区间中含小于的数那肯定不合法),每次在这些区间里面贪心选就行。

详见代码:


Code

#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2005;

int n, k, q;
int a[N];

bool chk(int x) {
    for(int i = 1; i <= n; i++) {
        int Min = a[i];
        int res = 0;
        for(int l = 1, r; l <= n; l = r) {
            r = l;
            if(a[l] < Min) {
                ++r; continue;
            }
            int cnt = 0;
            while(r <= n && a[r] >= Min) {
                if(a[r] <= Min + x) ++cnt;
                ++r;
            }
            if(r - l >= k) res += min(r - l + 1 - k, cnt);
        }
        if(res >= q) return true;
    }
    return false;
}

void run() {
    for(int i = 1; i <= n; i++) cin >> a[i];
    int l = 0, r = 1e9 + 1, mid;
    while(l < r) {
        mid = (l + r) >> 1;
        if(chk(mid)) r = mid;
        else l = mid + 1;
    }
    cout << l << '
';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(cin >> n >> k >> q) run();
    return 0;
}

F - Donation

题意:
给出一个无向图,选定一个起点,一开始有(W)元,之后可以选择走向相邻一个节点或者给当前结点捐赠(b_i)元,若走向一个结点至少得有(a_i)元才行。
现在确定一个最小的(W)以及一个起点,使得最终能够顺利捐赠成功每个点。

思路:
还有点没想清楚,先埋个坑。

以上是关于AtCoder Regular Contest 114 F Permutation Division的主要内容,如果未能解决你的问题,请参考以下文章

刷题AtCoder Regular Contest 001

AtCoder Regular Contest 094

[Atcoder Regular Contest 060] Tutorial

AtCoder Regular Contest 103

AtCoder Regular Contest 128

AtCoder Regular Contest 119 C