[CF1819D] Misha and Apples 题解

容易发现 \(k_i=0\) 时只有两种策略:

  1. 假如我这里不选,接下来不会再出现清空,且没有选入集合的集合为 \(T\),我们就让这个地方为 \(T\)
  2. 否则早死比晚死好,直接原地起爆,清空集合。

显然第一种决策只会在最后连续出现,所以我们找到最长不重复后缀 \([r,n]\),那么这一段区域里最早可以起爆的位置后,就是第一种决策的执行区间。

至于这个位置能不能原地起爆,考虑分成两种情况:

  1. \(k_i=0\),显然容易起爆。
  2. \(k_i\ne 0\),枚举集合内每一个数上一次出现的位置 \(j\)\(k=0\) 看作所有数都出现了),那么我们只要保证从 \(j\) 以前最后一个能起爆的位置 \(g_j\)\(i\) 形成的开区间 \((g_j,i)\) 能够不爆炸,我们就一定能够在 \(i\) 起爆,否则不行。

至于如何判断一段区间会不会起爆,考虑双指针维护最小右端点 \(l_i\),当 \(l_{i-1}\le g_j+1\) 时,这一段区间不会起爆。

然后我们找到 \([r-1,n]\) 中最小的 \(x\),满足 \(x\) 可以起爆。那么剩下集合的并集大小就是答案。假如后面的部分有 \(k_i=0\) 的情况,答案就是 \(m\)

注意没有保证 \(m\) 的范围,所以不能随便使用 \(m\)。时间复杂度是 \(O(\sum n)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5;
int t,n,m,k[N],f[N],g[N];
unordered_map<int,int>mp,ls;
vector<int>s[N];int rr;
void add(int id,int ad){
	for(auto x:s[id]) mp[x]+=ad;
}void solve(){
	for(int i=1;i<=n;i++)
		f[i]=g[i]=0,s[i].clear();
	cin>>n>>m,rr=n;
	int fl=1;mp.clear();
	for(int i=1;i<=n;i++){
		cin>>k[i];
		for(int j=1,x;j<=k[i];j++)
			cin>>x,s[i].push_back(x);
	}while(rr){
		for(auto x:s[rr])
			mp[x]?fl=0:mp[x]=1;
		if(!fl) break;rr--;
	}mp.clear(),ls.clear(),g[0]=-1;
	for(int l=1,r=1,lst=0,id=0;r<=n;r++){
		g[r]=lst,fl=0,add(r-1,1);
		if(!k[r]){f[r]=1,id=r,lst=r;continue;}
		for(auto x:s[r])
			if(g[max(ls[x],id)]+1>=l) fl=1;
		if(fl) f[r]=1,lst=r;
		for(auto x:s[r]) ls[x]=r;
		for(auto x:s[r])
			while(mp[x]) add(l++,-1);
	}while(!f[rr++]);int sum=0;
	for(int i=rr;i<=n;i++){
		if(k[i]){sum+=k[i];continue;}
		return cout<<m<<"\n",void();
	}cout<<sum<<"\n";
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t,f[0]=1;while(t--) solve();
	return 0;
}
posted @ 2025-02-21 21:38  长安一片月_22  阅读(7)  评论(0)    收藏  举报