题解:CF2185G Mixing MEXes
前言
数组开小调了两个小时。警示后人。
Description
给定
n 个数组a_1, a_2, \ldots, a_n 。对这些数组恰好执行一次如下操作:
- 在
a_1, a_2, \ldots, a_n 中任选一个数组。假设选择了a_i (1 \leq i \leq n )。- 在该数组
a_i 内任选一个元素。假设选择了a_i 的第j 个元素,记为a_{i,j} (1 \leq j \leq |a_i| ,其中|a_i| 表示数组a_i 的长度)。- 在其它数组中选一个(不能是
a_i 本身)。假设选择了a_k (1 \leq k \leq n, k \neq i )。- 将
a_{i,j} 加到数组a_k 的末尾,并且从a_i 中移除a_{i,j} 。- 本次操作
(i, j, k) 的价值定义为操作执行后所有数组的\operatorname{MEX} 之和。更正式一点,操作后的价值为\sum_{i=1}^{n} \operatorname{MEX}(a_i) 。计算所有可能的不同独立操作的价值之和。
Solution
处理出来每个序列的
发现只进行一次操作这个限制非常厉害。
因为数的总数很少,所以我们可以依次枚举每个数,计算挪走这个数对答案的贡献。
考虑把一个数
如果把
而不难发现,因为至多加入一个元素,所以每个序列加入其
我们就对这个东西开个桶记录一下总和就可以了,每个序列的新
Code
:::info[代码]
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3fll
using namespace std;
int n;
vector<int> a[200010];
int pmex[200010],nd[200010];
int bkt[1000010];
long long mexcnt[1000010],ndsum[1000010];
int calcmex(vector<int> v){
sort(v.begin(),v.end());
int nowmex=0;
for(int x:v){
if(nowmex==x) nowmex++;
else if(nowmex<x) break;
}
return nowmex;
}
int main(){
cin.tie(0)->sync_with_stdio(false);
int T;cin>>T;
while(T--){
cin>>n;
for(int i=1;i<=n;i++){
int l;cin>>l;
a[i].resize(l);
for(int &x:a[i]) cin>>x;
}
for(int p=1;p<=n;p++){
pmex[p]=calcmex(a[p]);
a[p].push_back(pmex[p]);
nd[p]=calcmex(a[p]);
a[p].pop_back();
}
long long mexsum=0;
for(int p=1;p<=n;p++){
mexsum+=pmex[p];
mexcnt[pmex[p]]++;
ndsum[pmex[p]]+=nd[p];
}
long long ans=0;
for(int p=1;p<=n;p++){
mexcnt[pmex[p]]--;
ndsum[pmex[p]]-=nd[p];
for(int x:a[p]) bkt[x]++;
for(int x:a[p]){
if(x<pmex[p]&&bkt[x]==1) mexsum-=pmex[p],mexsum+=x;
ans+=1ll*(n-1)*mexsum;
ans+=-1ll*mexcnt[x]*x+ndsum[x];
if(x<pmex[p]&&bkt[x]==1) mexsum+=pmex[p],mexsum-=x;
}
mexcnt[pmex[p]]++;
ndsum[pmex[p]]+=nd[p];
for(int x:a[p]) bkt[x]--;
}
cout<<ans<<"\n";
for(int p=1;p<=n;p++){
a[p].clear();
mexcnt[pmex[p]]--;
ndsum[pmex[p]]-=nd[p];
pmex[p]=nd[p]=0;
}
}
# ifdef TakanashiHoshino
cerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";
# endif
return 0;
}
:::