首页 > 其他分享 >AtCoder Regular Contest 114 F Permutation Division

AtCoder Regular Contest 114 F Permutation Division

时间:2023-04-22 09:14:00浏览次数:53  
标签:rt AtCoder vc lcp Contest int mid pos Division

洛谷传送门

AtCoder 传送门

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

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

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

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

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

预处理出以 \(a_1\) 开头,\(a_i\) 结尾的 \(\text{LDS}\),记为 \(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}\) 这种方案的 \(\text{LCP}\)。注意如果 \(\text{LCP}\) 相同,则先手想让 \(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;
}

标签:rt,AtCoder,vc,lcp,Contest,int,mid,pos,Division
From: https://www.cnblogs.com/zltzlt-blog/p/17342410.html

相关文章

  • AtCoder Regular Contest 114 D Moving Pieces on Line
    洛谷传送门AtCoder传送门挺有意思的题。首先显然地,一个棋子不会走回头路。于是一个棋子沿着边走的效果就是区间异或。更进一步,设\(s_i\)为\(i-1\toi\)的边颜色与\(i\toi+1\)的边颜色是否相同(差分),相当于对于每个\(i\)都选择\(s_{a_i}\)和\(s_{x_i}\),将它们异或......
  • 模拟赛 & VP & Contest 记录
    CatOJC140(初中)\(100+93+100+10=303\),Rank1。是个dp场,A题期望dp,B题神奇猜结论,C题换根dp,D题树上博弈dp。A题设\(f_u\)为填满子树\(u\)的期望次数,\(s_u\)为\(u\)子树大小,容易得到\(f_u=f_v+\frac{1}{s_u}\)。B题若\(n\)是偶数,考虑数列里随便取一个数将其......
  • Atcoder Beginner Contest 298---E
    题目:E-UnfairSugoroku(atcoder.jp)分析:这题状态转移方程挺好推的,但是用dp解是我没想到的dp[i][j][0]表示T在i点,A在j点且轮到T走时赢的概率dp[i][j][1]表示T在i点,A在j点且轮到A走时赢的概率代码:#include<bits/stdc++.h>usingnamespacestd;#definelllonglong#def......
  • AtCoder Regular Contest 109 F 1D Kingdom Builder
    洛谷传送门AtCoder传送门考虑判断一个终止态是否可达。如果只有一个棋子连续段那一定可达;否则就存在\(\ge2\)个连续段。此时把放棋子看成删除,那么限制就是如果删除一个孤立的棋子(两边没有棋子)且还有别的格子有棋子,这个棋子的颜色异于其他连续段的两边棋子的颜色。设第一......
  • ARC159F Good Division【性质,DP,线段树】
    定义一个序列是好的当且仅当其可以通过每次删去一对相邻的不同的数把序列删空。给定一个长度为\(2n\)的序列\(a\),求有多少种划分方式使得每一段都是好的。答案对\(998244353\)取模。\(n\leq5\times10^5\),时限\(\text{5.0s}\)。先考虑什么样的数列是合法的,显然必要条......
  • AtCoder Beginner Contest 298
    A-JobInterview#include<bits/stdc++.h>usingnamespacestd;intmain(){ intn; strings; cin>>n>>s; if(s.find("x")!=-1){ printf("No\n"); }elseif(s.find("o")==-1){ printf("No......
  • Atcoder Regular Contest 118 E - Avoid Permutations(容斥+DP)
    挺套路的DP。第一步是显然的:转换贡献体,DP一条从\((0,0)\)到\((n+1,n+1)\)的路径,然后计算有多少个排列满足这条路径不经过任何一个\((i,p_i)\)。正着统计肯定不好求,考虑容斥。即我们钦定一些路径上的点,满足这些点必须对应某个\((i,p_i)\),然后计算有多少个\(p\)符合这个......
  • Contest 23-04-18
    #D.糖果镇思路\(m=3\)时整个路径有两个拐点,分别是\(m=1\tom=2,m=2\tom=3\)设拐点\(1\)在第\(i\)列,拐点\(2\)在第\(j\)列,则路径上的数字总和为\((front[1][i])+(front[2][j]-front[2][i-1])+(back[j])\)(\(front[i][j]\)表示第i行\(1\toj\)的前缀和,\(back[j]\)表......
  • AtCoder Regular Contest 109 E 1D Reversi Builder
    洛谷传送门AtCoder传送门考虑固定\(s\)和每个格子的颜色,最终有多少个石子被染黑。结论:任何时刻只有不多于两个极大同色连通块。证明:设\([x,y]\)为当前的黑连通块,\([y+1,z]\)为白连通块。如果下一次染\(x-1\),若\(x-1\)为白,则\([x-1,z]\)都被染为白;否则\(x-1\)被......
  • AtCoder Regular Contest 109 D L
    洛谷传送门AtCoder传送门这种题根本做不出来……考虑一个L形怎么方便地表示出来。可以发现对于组成L形的三个点\((x_1,y_1),(x_2,y_2),(x_3,y_3)\),只要知道\(x=x_1+x_2+x_3\)和\(y=y_1+y_2+y_3\),就能确定三个坐标。证明是设折点坐标为\((p,q)\),则其余两......