[CF1819D] Misha and Apples 题解
容易发现 \(k_i=0\) 时只有两种策略:
- 假如我这里不选,接下来不会再出现清空,且没有选入集合的集合为 \(T\),我们就让这个地方为 \(T\)。
- 否则早死比晚死好,直接原地起爆,清空集合。
显然第一种决策只会在最后连续出现,所以我们找到最长不重复后缀 \([r,n]\),那么这一段区域里最早可以起爆的位置后,就是第一种决策的执行区间。
至于这个位置能不能原地起爆,考虑分成两种情况:
- \(k_i=0\),显然容易起爆。
- \(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;
}

浙公网安备 33010602011771号