CF 1437 Educational Codeforces Round 97

CF 1437

E. Make It Increasing

题意

给一个数组,固定某些位置不可动,改变可动位置,使得数组严格单调递增,求最少改变位置数。

思路

  1. 考虑没有固定位置时,令 b i = a i − i b_{i}= a_{i} - i bi=aii,若 b i b_{i} bi单调不增,则 a i a_{i} ai严格单调递增。求单调不减子序列,答案即是总长度减最大单调不减子序列长度。
  2. 当有固定位置时,将数组划分成若干段,对于每一段而言,首尾是固定的。那么强制首位必选,求单调不减子序列,找到一个值小于末尾位置的位置pos,使得以首位开始,pos结尾的最长不减子序列最长,答案即是总长度减该序列的长度。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 5e5 + 10;
const int inf = 1e9 + 5e5 + 10;
int a[N], q[N], f[N], lock[N], b[N];
int flag, ans;
int bisearch(int tail, int x)
{
	int l = 0;
	int r = tail + 1;
	while(r - l > 1)
	{
		int mid = (l + r) >> 1;
		if(b[q[mid]] <= x)
			l = mid;
		else
			r = mid;
	}
	return l;
}
void solve(int l, int r)
{
	if(a[r] - a[l]  + 1 < (r - l + 1) )
	{
		flag = 0;
		return;
	}
	for(int i = l + 1; i <= r; i++)
		b[i] = a[i] - i;
	q[1] = l;
	int tail = 1;
	f[l] = 1;
	for(int i = l + 1; i <= r; i++)
	{
		int pos = bisearch(tail, b[i]);
		if(pos)
		{
			f[i] = pos + 1;
			if(b[q[pos + 1]] >= b[i] || pos == tail)
				q[pos + 1] = i;
			tail = max(tail, pos + 1);
		}
	}
	int fans = 0;
	for(int i = l; i < r; i++)
		if(b[i] <= b[r] && f[i] + 1 > fans)
		{
			fans = f[i] + 1;
		}
	ans += r - l + 1 - fans;
}
int main()
{
	int n, m, x;
	flag = 1;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++)
	{ 
		scanf("%d", &a[i]);
	} 
	for(int i = 1; i <= m; i++)
	{
		scanf("%d", &x);
		lock[x] = 1;
	}
	a[0] = -inf;
	b[0] = -inf;
	a[n + 1] = inf;
	int j = 0;
	for(int i = 0; i <= n; i = j)
	{
		j = i + 1;
		while(j <= n && !lock[j])
			j++;
		solve(i, j);
	}
	if(flag == 0)
		printf("-1");
	else
		printf("%d", ans);
	return 0;
}

F.Emotional Fishermen

题意

n个渔夫,第i个渔夫捕了 a i a_{i} ai条鱼。将这些渔夫排成一列,使得对于每个渔夫而言,满足如下两个条件之一:

  1. 他前面的任意一个渔夫捕获的鱼的数量的二倍小于等于他捕获的数量,我们称他是开心的。
  2. 他前面存在一个渔夫捕获的鱼的数量大于等于他捕获的数量的二倍,我们称他是伤心的。
    求符合条件的排列的数量。

思路

一道巧妙的计数DP。

一个结论

在任意符合条件的排列中,所有“开心”的渔夫构成一个单调上升子序列,且任意两个开心的渔夫之间相差二倍以上。其他的渔夫都是伤心的。
例如:2,1,1,4,2,2,8,3,3,20,其中加粗的是开心的渔夫,其他的是伤心的渔夫。

DP状态

需要记录两个维度:当前前缀的捕获量最大的是哪一个渔夫(设为i)和当前前缀的长度(设为j)

状态转移
  1. 增加一个伤心的渔夫:他的捕获量要小于等于 a i 2 \frac{a_{i}}{2} 2ai,但又不能在前面出现过。观察发现,前面出现过的渔夫的捕获量都是小于等于 a i 2 \frac{a_{i}}{2} 2ai的,所以满足要求的渔夫数量为cntless(i)-j+1个,其中cntless(i)代表捕获量小于等于 a i 2 \frac{a_{i}}{2} 2ai的渔夫数量。将渔夫按照捕获量排序,然后二分就可以求出cntless(i)。
  2. 增加一个开心的渔夫:他的捕获量大于等于 2 a i 2a_{i} 2ai即可。即对于渔夫k,如果渔夫i满足 a k 2 ≥ a i \frac{a_{k}}{2} \ge a_{i} 2akai,那么就可以转移,这一部分可以用前缀和优化,在利用cntless(i)即可求出。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int N = 5010;
const LL mod = 998244353; 
LL f[N][2], sum[N];
int a[N];
int get_les(int i)
{
	int l = 0;
	int r = i + 1;
	while(r - l > 1)
	{
		int mid = (l + r) >> 1;
		if(a[mid] * 2 <= a[i])
			l = mid;
		else
			r = mid;
	}
	return l;
}
int main()
{
	int n;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= n; i++)
		f[i][1] = 1;
	for(int j = 1; j <= n; j++)
	{
		for(int i = 0; i <= n; i++)
			sum[i] = 0, f[i][(j+ 1) % 2] = 0;
		for(int i = 0; i <= n; i++)
		{
			int p = get_les(i);
			if(p >= j)
				f[i][(j + 1) % 2] = (f[i][(j + 1) % 2] + f[i][j % 2] * (LL)(p - j + 1) % mod) % mod;
			sum[i] = (sum[i - 1] + f[i][j % 2]) % mod;
			f[i][(j + 1) % 2] = (f[i][(j + 1) % 2] + sum[p]) % mod;
		}
	}
	cout << f[n][n % 2]<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值