CFEducational Codeforces Round 66题解报告

Posted wyxdrqc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CFEducational Codeforces Round 66题解报告相关的知识,希望对你有一定的参考价值。

CFEducational Codeforces Round 66题解报告

感觉丧失了唯一一次能在CF上超过wqy的机会QAQ

A

不管

B

不能直接累计乘法打\(tag\),要直接跳

C

考虑二分第\(k\)小的值

那么问题就变成了

每一个数变成了\([x-mid,x+mid]\)的一段区间,如果有一个位置被覆盖了超过\(k\)

那么\(mid\)一定合法

类似括号匹配

每次碰到左端点就贡献+1

右端点就统计答案然后-1

维护答案的同时顺便维护位置就好了

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 4e5 + 3;
int a[N];
int n,k,ans;
vector <pii> G;
inline int read()
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch))
        if(ch == '-') c = -1;
        ch = getchar();
    
    while(isdigit(ch))
        v = v * 10 + ch - 48;
        ch = getchar();
    
    return v * c;

inline bool check(int mid)
    G.clear();
    for(int i = 1;i <= n;++i) G.push_back(mk(a[i] - mid,0)),G.push_back(mk(a[i] + mid,1));
    sort(G.begin(),G.end());
    int now = 0;
    for(int i = 0;i < (int)G.size();++i)
        if(G[i].se == 0) now++;
        else
            if(now >= k)ans = G[i].fi;return 1;
            now--;  
        
    
    return 0;

int main()
    int T = read();
    while(T--)
        n = read(),k = read() + 1;
        for(int i = 1;i <= n;++i) a[i] = read();
        int l = 0,r = 1e9;
        while(l <= r)
            int mid = (l + r) >> 1;
            if(check(mid)) r = mid - 1;
            else l = mid + 1;
           
        printf("%d\n",ans);
    
    return 0;

D

DP方法非常显然

但是时间复杂度不对

又没有凸性,无法优化(至少我不会)

只能考虑别的方法

发现每一次分段其实就是加上某一个后缀的值

很显然某个后缀只能加一次

所以题目变成了求\([2,n]\)的前\(k - 1\)大的后缀的和

最后将答案加上权值总和

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#define LL long long
#define pii pair<LL,LL>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 5e5 + 3;
const LL INF = 1e15;
LL a[N];
LL sum[N];
LL maxx[N];
int n,k;
int tag[N];
priority_queue <pii> q;
inline int read()
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch))
        if(ch == '-') c = -1;
        ch = getchar();
    
    while(isdigit(ch))
        v = v * 10 + ch - 48;
        ch = getchar();
    
    return v * c;

int main()
    
    n = read(),k = read();
    for(int i = 1;i <= n;++i) a[i] = read(),maxx[i] = -INF;
    maxx[n] = sum[n] = a[n];
    q.push(mk(sum[n],n));
    for(int i = n - 1;i >= 1;--i)
    //  cout << i << endl;
        sum[i] = sum[i + 1] + a[i];
        maxx[i] = max(maxx[i + 1],sum[i]);
        if(i != 1) q.push(mk(sum[i],i));
    
    LL ans = sum[1];
    //cout << "GG" << endl;
    for(int i = 1;i < k;++i,q.pop())
        ans += q.top().fi;
    
    cout << ans;
    return 0;

F

方法一;考虑分治

题目中要求的区间转化一下就是下面两个条件

区间\([l,r]\)合法当且仅当

\(1\) 区间最大值为\(r - l + 1\)

\(2\) 区间无重复元素

我们考虑每次按照最大值去分治

考虑跨过最大值的贡献

接下来要满足第二个条件

我们设\(pre_i\)\(a_i\)上一次出现的位置

\(max_i\)\(pre_i\)的前缀max

发现区间\([l,r]\)无重复元素的意思是

\(max_r < l\)

就是每个数上一次出现的位置都在\(l\)左边

由于区间最大值就是区间长度

我们就可以通过枚举一边寻找另一边的方式求解

方法二;

我们给每一个数随机分配一个\(128\)位的数字

所以区间\([L,R]\)符合条件

就可以用前缀异或去表示

由于合法区间一定包含\(1\)

我们就从一个\(1\)开始到下一个\(1\)为止去寻找最大值在右边的贡献

之后把数组反过来再来一遍

这样\(1\)可能会被计算两边,特判就好。

以上是关于CFEducational Codeforces Round 66题解报告的主要内容,如果未能解决你的问题,请参考以下文章

codeforces上怎么看测试数据

如何看codeforces做了多少题

codeforces上怎么看测试数据

codeforces比赛后怎么看题解和答案

codeforces是啥?

codeforces Codeforces 650A Watchmen