Codeforces1631D. Range and Partition
传送门
题目大意
给定一个长为 \(n(1\leq n\leq 2\times 10^5)\) 的序列 \(a(1\leq a_i\leq n)\), 要将其划分为 \(k(1\leq k\leq n)\) 个子段,每个字段需要满足字段中在 \([x,y]\) 内的数字数量大于不在的数量,求出 \(y-x\) 最小的 \([x,y]\) 以及划分方式。
思路
考虑到对于一个区间,如果能够划分处 \(>k\) 个合法的字段,我们只要合并一些字段,显然其仍然合法,就可以得到正好 \(k\) 个字段,我们可以从前往后考虑,记在第 \(i\) 个字段在区间内的数字数量为 \(in_i\) ,不在的为 \(out_i\) ,于是对于前 \(k-1\) 个字段有 \(in_i=out_i+1\) ,对于最后一个字段有 \(in_k\geq out_k+1\) ,于是可以得到能够划分出 \(k\) 个满足要求的字段的条件为 \(\sum_{i=1}^{k}in_i\geq\sum_{i=1}^{k}out_i+k\) ,也就是整个序列中,在区间内的数字总数 \(\geq \lfloor\frac{n+k+1}{2}\rfloor\) 。我们可以求出序列中每个数字的个数并求其前缀和 \(s\) ,于是问题相当于找出一个最小的区间 \([x,y]\) ,使得 \(s_y-s_{x-1}\geq \lfloor\frac{n+k+1}{2}\rfloor\) ,可以用双指针在 \(O(n)\) 内解决,最后从前向后推出字段的划分方式即可。
代码
#include
#include
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair PII;
#define all(x) x.begin(),x.end()
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 100000000;
const LL mod = 998244353;
const int maxn = 200010;
int T, N, K, A[maxn], cnt[maxn], S[maxn];
void solve()
{
int need = (N + K + 1) / 2;
int ans = inf, ax = 0, ay = 0;
int l = 1, r = 1;
while (l <= N)
{
while (r <= N && S[r] - S[l - 1] < need)
r++;
if (r == N + 1)
break;
if (r - l < ans)
ans = r - l, ax = l, ay = r;
l++;
}
cout << ax << ' ' << ay << endl;
if (K == 1)
{
cout << 1 << ' ' << N << endl;
return;
}
int cnt = 0, in = 0, out = 0;
for (int i = 1; i <= N; i++)
{
for (int j = i; j <= N; j++)
{
if (A[j] >= ax && A[j] <= ay)
in++;
else
out++;
if (in > out)
{
in = out = 0;
cnt++;
cout << i << ' ' << j << endl;
if (cnt == K - 1)
{
cout << j + 1 << ' ' << N << endl;
return;
}
i = j;
break;
}
}
}
}
int main()
{
IOS;
cin >> T;
while (T--)
{
cin >> N >> K;
for (int i = 1; i <= N; i++)
cnt[i] = S[i] = 0;
for (int i = 1; i <= N; i++)
{
cin >> A[i];
cnt[A[i]]++;
}
for (int i = 1; i <= N; i++)
S[i] = S[i - 1] + cnt[i];
solve();
}
return 0;
}