CF968 (Div. 2)D1和D2题解

D1题目链接:

https://codeforces.com/contest/2003/problem/D1

D2题目链接:

https://codeforces.com/contest/2003/problem/D2

D1思路:根据数据范围\suml<=2*1e5; 我们可以对于每个l_{i}进行暴力枚举出mex(最小未出现非负数),记作发f[i];枚举出mex后,考虑当f(i),i=mex时,所以我们同时需要枚举出:补齐mex后的mex,记作g[i];

下面考虑最大的g[i],由于可以枚举多次,对于最大的(f[i],g[i])对,我们不妨考虑 0<=j<=m,此时当j!=f[i]时,我们可以通过使用两次l_{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]);但是显然这不是最优解,实际上我们可以考虑这样一种情况,我们可以通过某一种顺序使用l_{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;
}

  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,根据提供的引用内容,我无法理解你具体想要问什么问题。请提供更清晰明确的问题,我将竭诚为你解答。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Codeforces Round 860 (Div. 2)题解](https://blog.csdn.net/qq_60653991/article/details/129802687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【Codeforces】 Codeforces Round 865 (Div. 2) (补赛)](https://blog.csdn.net/t_mod/article/details/130104033)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Codeforces Round 872 (Div. 2)(前三道](https://blog.csdn.net/qq_68286180/article/details/130570952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值