码迷,mamicode.com
首页 > 其他好文 > 详细

CFEducational Codeforces Round 66题解报告

时间:2019-08-20 22:13:27      阅读:90      评论:0      收藏:0      [点我收藏+]

标签:inf   getch   make   get   mat   algorithm   条件   区间   namespace   

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题解报告

标签:inf   getch   make   get   mat   algorithm   条件   区间   namespace   

原文地址:https://www.cnblogs.com/wyxdrqc/p/11385714.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!