首页 > 其他分享 >Codeforces Round 963 (Div. 2) D题解

Codeforces Round 963 (Div. 2) D题解

时间:2024-08-05 17:17:13浏览次数:18  
标签:状态 963 int 题解 mid 数组 操作 Div dp

Codeforces Round 963 D

题目描述

给定两个正整数 nk 以及另一个由 n 个整数组成的数组 a
在一次操作中,可以选择 a 中大小为 k 的任意一个子数组,然后将其从数组中删除,而不改变其他元素的顺序。更正式地说,假设 (l, r) 是对子数组 a[l], a[l+1], ..., a[r] 的操作,使得 r-l+1=k ,那么执行此操作意味着将 a 替换为 [a[1], ..., a[l-1], a[r+1], ..., a[n]]
例如,如果 a=[1, 2, 3, 4, 5] 在这个数组上执行了 (3, 5) 操作,那么它就会变成 a=[1, 2] 。此外,操作 (2, 4) 的结果是 a=[1, 5] ,操作 (1, 3) 的结果是 a=[4, 5]
a 的长度大于 k (即 |a| > k )时,你必须重复操作。处理后,数组 a 中所有剩余元素的最大中值是多少?
长度为 n 的数组的中位数是我们对元素进行非递减排序后索引为 ⌊ (n+1)/2 ⌋ 的元素。例如 median([2, 1, 5, 4, 3]) = 3median([5]) = 5median([6, 8, 2, 4]) = 4

解题思路

  1. 这题需要想到二分搜索可行解,复杂度在log(Max(a)).
  2. 需要理解,这题找的是最大解,所以对于二分的每一个x,只需要找是否有某个终态满足 其中的>=x的数字多于一半。例如1,2,4,5,5,6。对于4是满足的,对于5不满足。同时对于1也满足,可能会存在疑惑,1不可能是中位数,但是我们找的实际上是最大解,所以1可行的含义应该是这个解可以>=1。如果满足,二分会一直向后推导到4也就是答案。因为比4大的所有x都被认为不满足。
  3. 考虑如何进行判断,这里我们使用dp来访问可达状态,我们的目标应该是保证到达终态时>=x的数尽量的多。这时候其实就是看一个序列大于等于x的数有多少个。
  4. 考虑如何转移状态,这里我们对编号减一并且取模(把index都减1了方便后续判断)。对于每一个i,它可以由哪里转移过来?没有任何数字时>=x的数量为0设dp[0] = 0。第一个状态很显然,它可以由dp[0]转移而来,所以dp[1] = dp[0]+ (a[1] >= x ? 1 : -1)
  5. 为啥这里取-1和1,因为这样最后只要大于0就可以说明>=x数量多余<x,方便判断
  6. 对于不在第一行的序号,它首先可以继续由它左方的状态转移而来,并且它还可以放弃自身的位置,也就是继续让上一行的状态保留。
    dp[i] = max(dp[i-1] + (a[i] >= x ? 1 : -1), dp[i-k])
  7. 但是对于每一行的第一个点,没有左端的状态给他转移,也就是它不能接续前面的序列,因为这个序列长度它不能>k。它现在面临两个选择,第一是新开一个序列,也就是把前一个状态ci更新为0,或者它还可以和其它点一样,放弃自己,继续继承上一行的0位置。表现在操作上就是index 1 2 3 4 选择删掉1 2 3还是删掉2 3 4。
  8. 至此整个题目就解决了。以下面的样例举例,实际上就是告诉你只能选两个位于0和1的数,并且0和1需要满足一个顺序,0选择的数字在a[i]中必须出现在1的前方。朴素做法是枚举所有可能组合来选择,但是最大数量是可以逐步转移过去的,所以只需要在n次操作里面dp。
n = 8, k = 3
index 1 2 3 4 5 6 7 8
mod   0 1 2 0 1 2 0 1

拆成多段 状态只能由左向右转移或者正下方替代正上方的状态
因为每次要删去连续的k个,可以亲手试试来验证。
0->1->2
|  |  |
0->1->2
|  | 
0->1

代码实现

#include <bits/stdc++.h>
using namespace std;
#define __DEBUG
#ifdef __DEBUG
#define DB(X) { cout << #X << " = " << (X) << '\n'; }
#define DB1(A, _) { cout << #A << "[" << _ << "] = " << (A[_]) << '\n'; }
#define DB2(A, _, __) { cout << #A << "[" << _ << "][" << __ << "] = " << (A[_][__]) << '\n'; }
#define DB3(A, _, __, ___) { cout << #A << "[" << _ << "][" << __ << "][" << ___ << "] = " << (A[_][__][___]) << '\n'; }
#define PR(A, l, r) { cout << '\n'; for (int _ = l; _ <= r; ++_) DB1(A, _); cout << '\n';}
#else
#define DB(X)
#define DB1(A, _)
#define DB2(A, _, __)
#define DB3(A, _, __, ___)
#define PR(A, l, r)
#endif
#define sz(x) ((int) (x).size())
#define fi first
#define se second
#define int long long
#define ULL unsigned long long
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
const int p=998244353;
int po(int a,int b) {if(b==0) return 1; if(b==1) return a; if(b%2==0) {int u=po(a,b/2);return (u*1LL*u)%p;} else {int u=po(a,b-1);return (a*1LL*u)%p;}}
int gc(int a, int b) {if(a == 0) return b;if(b == 0)return a;return gc(b, a%b);}
int exgcd(int a,int b,int &x,int &y){if(b==0){x=1;y=0;return a;}int r=exgcd(b,a%b,x,y);int temp=y;    y=x-(a/b)*y;x=temp;return r;}
mt19937_64 rnd(chrono::steady_clock::now().time_since_epoch().count());

void Solve(){
	int n, k;cin>>n>>k;
	int a[n+1];
	for(int i = 1; i <= n;i ++){
		cin>>a[i];
	}
	int l = 0, r = 1e9 + 10, mid;
	while(l < r){
		mid = (l + r + 1) >> 1;
		vector<int> dp(n+1, -LLONG_MAX);
		dp[0] = 0;
		for(int i = 1; i <= n; i++){
			int ci;
			ci = dp[i-1];
			if((i-1) % k == 0) ci = 0;
			int is = a[i] >= mid ? 1 : -1;
			dp[i] = max(dp[i], ci + is);
			if(i > k) dp[i] = max(dp[i], dp[i - k]);
		}
		//DB(l)DB(r)DB(mid)
		//PR(dp,0,n)
		if(dp[n] > 0){
			l = mid;
		}else{
			r = mid - 1;
		}
	}
	cout<<l<<endl;
}

int32_t main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T;cin>>T;while(T--)
		Solve();
	return 0;
}

复杂度分析

  • 时间复杂度: nlog(max(a))

标签:状态,963,int,题解,mid,数组,操作,Div,dp
From: https://www.cnblogs.com/luter/p/18343638

相关文章

  • leetcode200. 岛屿数量C++题解,精美图例和流程图,一题带你弄懂图的dfs遍历算法
    leetcode200.岛屿数量给你一个由‘1’(陆地)和‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。示例1:输入:grid=[[“1”,“1”,“1”,......
  • [POI2015] POD 题解
    前言题目链接:洛谷。题意简述长度为\(n\)的一串项链,每颗珠子是\(k\)种颜色之一。第\(i\)颗与第\(i-1,i+1\)颗珠子相邻,第\(n\)颗与第\(1\)颗也相邻。切两刀,把项链断成两条链。要求每种颜色的珠子只能出现在其中一条链中。求方案数量(保证至少存在一种),以及切成的两段......
  • CF1993C Light Switches 题解
    CF1993CLightSwitches题解题目大意有\(n\)盏灯,第\(i\)盏灯亮着的时间为\([a_i+bk,a_i+(b+1)k-1]\),其中\(k\)为给定常数,\(b\)为任意非负偶数。求一个最小的\(t\),使得在时间\(t\)所有灯都是亮着的。Solve令\(m=2k\),显然所有灯的开关状态以\(m\)为周期,所以我们......
  • P5086 的题解
    (一)将输入的四个数差分得到三个值,这三个值相同的两个坐标符合条件。用map存储记录这三个值的结构体,然后用vector存储下标。(二)AC代码。#include<bits/stdc++.h>#definedbdouble#definepbpush_back#definefifirst#definesesecond#definemkpmake_pair#defin......
  • [ARC118C] Coprime Set 题解
    题目传送门(洛谷)题目传送门(atcoder)Step1理解题意输入一个数nnn要求你构造一个长度为n......
  • 洛谷P10839 【MX-J2-T0】Turtle and Equations题解
    灰常简单!蒟蒻带您写代码!题目理解题目传送门题目描述给你四个正整数。现在你有一条算式。你需要判断能否在两个方框内分别填入三种运算符 之一(运算符可以重复使用),使得算式运算的结果等于。题目分析分析后我们能够发现,只要一一列举出所有能够输出的情况,剩下的输出即可......
  • 若依框架导入阿里OSS报错问题解决方案
    [INFO]ruoyi-quartz.......................................FAILURE[0.504s][INFO]ruoyi-generator....................................SKIPPED[INFO]ruoyi-admin........................................SKIPPED[INFO]---------------------------------......
  • div中添加el-loading(局部loading的使用)
    div中添加el-loading(局部loading的使用)效果:在div中实现el-loadinghttps://img-blog.csdnimg.cn/c2870e74bd344b06ad1ccb0844b8e8ce.png<divclass="content-main">{{hotList}}</div>getHotList(columnType){this.$nextTic......
  • 洛谷P4910题解
    题目大意现在穿T次手串,每根手串的长度分别为不同的n,有木和金两种珠子,相邻两颗珠子必须有一个是金。题目思路分析我们现在设穿到第n个珠子时用金的方案数为f[1][n],用木的方案数为f[0][n]如果第n个珠子为金,那么前一颗珠子是什么都可以,因此f[1][n]=f[1][n-1]+f[0][n-1]而如果......
  • Codeforces Round 963 (Div. 2) 补题记录(A~D,F1)
    不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.不会做F1.A直接计算每一个选项最多对多少个题加起......