D1题目链接:
https://codeforces.com/contest/2003/problem/D1
D2题目链接:
https://codeforces.com/contest/2003/problem/D2
D1思路:根据数据范围l<=2*1e5; 我们可以对于每个进行暴力枚举出mex(最小未出现非负数),记作发f[i];枚举出mex后,考虑当f(i),i=mex时,所以我们同时需要枚举出:补齐mex后的mex,记作g[i];
下面考虑最大的g[i],由于可以枚举多次,对于最大的(f[i],g[i])对,我们不妨考虑 0<=j<=m,此时当j!=f[i]时,我们可以通过使用两次集合,构造出g[i],同时对于j>g[i]的数来说,最大的值为不进行运算。
所以d1的代码呼之欲出
#include <bits/stdc++.h>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using namespace std;
const int N = 2*1e5 + 10, mod = 998244353;
void solve() {
int n, m;
cin >> n >> m;
vector<int> f(n), g(n);
for (int i = 0; i < n; i++) {
int num;
cin >> num;
vector<int> temp(num,0);
for (int j = 0; j < num; j++) {
cin >> temp[j];
}
//去重
sort(temp.begin(), temp.end());
temp.erase(unique(temp.begin(),temp.end()),temp.end());
num=temp.size();
int cnt = 0;
for (int j = 0; j < N; j++) {
if (cnt < num && temp[cnt] == j) cnt++;
else {
f[i] = j;
break;
}
}
int tt = 1;
while (cnt < num && temp[cnt] == f[i] + tt) {
tt++;
cnt++;
}
g[i] = f[i] + tt;
}
int maxnum=0;
for (int i = 0; i < n; i++) {
maxnum=max(maxnum,g[i]);
}
LL res = 0;
//防止爆LL
res=res+(LL)maxnum*(min(maxnum+1,m+1));
if(m>maxnum){
res=res+(LL)(m+maxnum+1)*(m-maxnum)/2;
}
cout << res << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D2思路:
相比较D1,这时候每组运算只能运算一次, 这时候我们不妨考虑最大的f[i]与g[i].
我们可以知道对于0<=j<m ,res+=max(j,f[i]);但是显然这不是最优解,实际上我们可以考虑这样一种情况,我们可以通过某一种顺序使用,使得f(j)>max(j,f[i]),这样的情况出现在“连续跳的时候”,那么此时我们可以区分出两种情况。j=f[i],j!=f[i];
当j=f[i]时,他可以直接跳;
当j!=f[i]时,说明需要f[i]出现两次以上(代表两个l),这样第一次我们可以调整j=f[i],第二次就是开始跳.
同时注意到一个性质,g[i]>f[i],说明我们必然是往后面跳,但是可能我们跳到某个g[i]后,这个g[i]还不是最大值,我们还可以跳,所以我们可以从后面往前面处理,先处理出后面能达到的最远位置。再进行状态更新,可能这时候会有所疑惑:当我们跳到某个g[i]后,如果我们还可以从g[i]处继续跳,那么不会重复利用到跳过的l吗,实际上是不会的,如果说我们重复利用了某个集合,那么我们从g[i]开始的话,那么必然会用到f[x]<g[i],同时f[x]出现过,那么f[x]可达的最大距离就是g[i],这一点与我们距离会变大相矛盾,因此不会出现重复利用;
可以知道,当我们处理出一条最大路径后,res+=max(dp[j],maxnum);maxnum为最大路径的值
同时当j>max(g[i])后
结果为res+=(LL)(m+maxnum1+1)*(m-maxnum1)/2;
代码如下
#include <bits/stdc++.h>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using namespace std;
const int N = 2*1e5 + 10, mod = 998244353;
typedef long long LL;
void solve() {
int n, m;
cin >> n >> m;
vector<int> f(n), g(n);
for (int i = 0; i < n; i++) {
int num;
cin >> num;
vector<int> temp(num,0);
for (int j = 0; j < num; j++) {
cin >> temp[j];
}
sort(temp.begin(), temp.end());
temp.erase(unique(temp.begin(),temp.end()),temp.end());
num=temp.size();
int cnt = 0;
for (int j = 0; j < N; j++) {
if (cnt < num && temp[cnt] == j) cnt++;
else {
f[i] = j;
break;
}
}
int tt = 1;
while (cnt < num && temp[cnt] == f[i] + tt) {
tt++;
cnt++;
}
g[i] = f[i] + tt;
}
int maxnum1=0;
int maxnum2=0;
for (int i = 0; i < n; i++) {
maxnum1=max(maxnum1,g[i]);
maxnum2=max(maxnum2,f[i]);
}
vector<int >st(maxnum1+2,0);
vector<int>e[maxnum1+2];
for (int i = 0; i < n; i++) {
st[f[i]]++;
e[f[i]].push_back(g[i]);
}
vector<int>dp(maxnum1+2,0);
for(int x=maxnum1; x>=0; x--){
dp[x]=x;
for(auto v:e[x]){
dp[x]=max(dp[x],dp[v]);
}
if(st[x]>1){
maxnum2=max(maxnum2,dp[x]);
}
}
LL res = 0;
for (int i = 0; i <= min(m,maxnum1); i++) {
res=res+max(dp[i],maxnum2);
}
if(m>maxnum1){
res=res+(LL)(m+maxnum1+1)*(m-maxnum1)/2;
}
cout << res << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}