首页 > 其他分享 >CF1886F

CF1886F

时间:2024-02-24 09:03:04浏览次数:23  
标签:cnt CF1886F ++ 枚举 hack 失效 部分

F. Diamond Theft

题目大意

有 \(2\) 颗钻石,有 $ n $ 个监控,每个监控有两个参数 $ s $ , $ t $ , $ s=1/2/3 $ 分别表示它是监视第一/第二/两个钻石, $ t $ 表示它被hack后会失效 $ t $ 秒,形式化,若在x秒hack了 $ i $ 监控,他会在 $ x+1\sim x+t $ 秒失效,有个小偷,他每秒会干以下三件事之一:

  1. do nothing
  2. hack 某个监控
  3. 偷走一颗钻石(要求先偷第一颗,再偷第二颗)

问最早能再什么时候偷完钻石,若无法偷完两颗,输出 $ -1 $

solution

首先考虑贪心,肯定不会do nothing,然后 $ s=1/2 $ 的监控一定只会被hack一次,所以就有个简单的想法先把s=1和3给hack掉,偷走第一颗,然后再hack $ s=2 $ 和已经失效的 $ s=3 $ ,再偷走第二颗

真的这么简单?Jiangly都打了1h的题目,考场只有3人爆切...所以手模发现一些hack。如果我在偷第一颗前hack一些 $ s=2 $ ,然后失效的 $ s=3 $ 可能会少一点,答案更优

我们还是捋一下思路,hack( $ s=1/3 $ 的全部+部分 $ s=2 $ ),偷走第一颗,hack( $ s=2 $ 的剩下+失效 $ s=3 $ ),偷走第二颗

所以我们发现就是要最小化 $ s=3 $ 失效的个数

然后就考虑设计一个数据结构快速判断我们的分配是否合法,简单来说,就是设 $ cnt_i $ 表示距离偷走钻石的时间为i内必须hack掉的监控数量,若存在 $ i-cnt_i $ 小于 $ 0 $ ,就意味着不合法,所以我们可以建两棵线段树维护两颗钻石,初值设为 $ 1,2,3... $ ,然后加入了一个监控即是区间减法,询问就是全局最值

接着贪,考虑放进第一部分的 $ s=2 $ ,一定是最大的几个,失效的 $ s=3 $ ,一定是最小的几个

于是我们开始枚举放几个 $ s=2 $ 在第一部分,然后再从大到小遍历 $ s=3 $ ,若能能不失效,就尽量不失效,也就是能只放第一部分就不放在两个部分,所以 $ O(n^2log) $ ???

我们发现 $ s=3 $ 如果放在只放在第一部分,因为它要满足还能偷第二颗钻石,所以还要减去一个系数(就是第二部分需要的操作数),我们还要枚举它,就变成 $ O(n^3log) $

我们发现因为要枚举系数,这中枚举顺序变得很鸡肋,考虑改变枚举顺序

所以先枚举第二部分需要的操作数,然后考虑从大到小遍历 $ s=3 $ ,贪心放进去

再从小到大枚举放几个 $ s=2 $ 在第一部分,但这个时候,如果我们发现放入了新的 $ s=2 $ 不满足条件,就考虑微调一下一下原来的操作,我们记录一下不满足条件的位置,在它之前找到一个最近的 $ s=3 $ ,并把它变成失效的。

可以发现这样是 $ O(n^2\log n) $

可以通过本题,但不够...

我们发现仔细回味一下上面的流程,发现有很多奇怪的地方(可能是因为他是官方),但它给的启发够多了,然后还发现有好多人爆标了,并不一定要用数据结构

也是先枚举再从小到大枚举放几个 $ s=2 $ 在第一部分,然后放 $ s=3 $ 的时候考虑一种新的方法:先把全部当作不失效的放进去,然后顺着跑一边,累计和,若不合法,单调栈找到一个最近的,然后把它当作失效的

对于下一个部分,我们发现我们可以确定的 \(s=2\) 的位置,于是改一下我们维护的东西,把第一部分接上第二部分,就可以不分 $ s=2 $ 在哪个部分

然后就可以贪心了, \(s=2\) 的位置已经固定了, \(s=3\)就直接放进去即可 ,于是就是 $O(n^2) $

Code

#include<bits/stdc++.h>
#define Fu(i,a,b) for(register int i=a;i<=b;i++)
using namespace std;
int n,s,t,g[4][1505],cnt[3005],mx,ans=0x7fffffff;
int bj[1505],z[1505];
void solve(int t){
	memset(cnt,0,sizeof(cnt));
	memset(bj,0,sizeof(bj));
	Fu(i,1,g[1][0]) cnt[g[1][i]]++;
	Fu(i,1,g[3][0]) cnt[max(0,g[3][i]-t-1)]++;
	int o=1,top=0,tmp=0;
	Fu(i,0,mx){
		if(i) cnt[i]+=cnt[i-1];
		while(o<=g[3][0]&&g[3][o]-t-1<=i) z[++top]=o++;
		while(top&&cnt[i]>i) cnt[i]--,cnt[g[3][z[top]]]++,bj[z[top--]]=1,tmp++;
		if(cnt[i]>i) return ;
	}memset(cnt,0,sizeof(cnt));
	cnt[t+1]++;
	Fu(i,1,g[1][0]) cnt[min(mx,g[1][i]+t+1)]++;
	Fu(i,1,g[2][0]) cnt[g[2][i]]++;
	Fu(i,1,g[3][0]){
		cnt[g[3][i]]++;
		if(bj[i]) cnt[min(mx,g[3][i]+t+1)]++;
	}
	Fu(i,0,mx){
		if(i) cnt[i]+=cnt[i-1];
		if(cnt[i]>i) return ;
	}ans=min(ans,tmp);
}
int main(){
	scanf("%d",&n),mx=(n<<1)+2;
	Fu(i,1,n) scanf("%d%d",&s,&t),g[s][++g[s][0]]=t;
	sort(g[3]+1,g[3]+g[3][0]+1);
	Fu(i,0,n) solve(i);
	if(ans>n) printf("-1");
	else printf("%d",ans+n+2); 
	return 0;
}

标签:cnt,CF1886F,++,枚举,hack,失效,部分
From: https://www.cnblogs.com/zhy114514/p/18028266

相关文章