Educational Codeforces Round 66 (Rated for Div. 2) Jun/05/2019 22:35UTC+8

比赛链接 https://codeforces.com/contest/1175
比赛记录 https://blog.csdn.net/cheng__yu_/article/details/105395197

B. Catch Overflow! (模拟栈操作)

在这里插入图片描述

链接:https://codeforces.com/contest/1175/problem/B

题意:给出 q 个指令,3 种类型:

  • for n :循环 n 次
  • end :结束一层循环
  • add :对 x 加 1

x 初始为 0,输出 x 最终的值。如果 x 大于 2 32 − 1 2^{32}−1 2321 ,则输出 “OVERFLOW!!!”

思路:用栈来模拟循环的指令

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const ll lim=(1ll<<32)-1;
int q,x;
ll sta[maxn],top=0,ans;
string op;
int main()
{
    bool ok=1;
    cin>>q;
    sta[0]=1;
    while(q--)
    {
        cin>>op;
        if(op=="add")
        {
            ans+=sta[top];
            if(ans>lim) ok=0;
        }
        else if(op=="for")
        {
            cin>>x;
            sta[++top]=min(sta[top-1]*x,lim+1);
        }
        else top--;
    }
    if(!ok) puts("OVERFLOW!!!");
    else cout<<ans<<"\n";
    return 0;
}

C. Electrification (绝对值 + 思维)*

在这里插入图片描述
链接:https://codeforces.com/contest/1175/problem/C

题意:给定 n 、 k n、k nk 长度为 n 的数组 a a a a i a_i ai 表示 X X X 轴上的点。请你找一个整数 x x x ,使得第 k + 1 k+1 k+1 小的 ∣ a i − x ∣ |a_i-x| aix 最小,可能有多个解,输出任何一个解

思路 ∣ a i − x ∣ |a_i-x| aix 就是 a i a_i ai x x x 的距离。要想使得第 k+1 个值最小,只需要取 k+1 个最近的点,然后取它们的中点就好了。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
int t,n,k;
int a[maxn];
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>k;
        for(int i=1;i<=n;++i) cin>>a[i];
        ll dis=9e18,ans;
        for(int i=1;i+k<=n;++i)
        {
            if(dis>a[i+k]-a[i])
            {
                dis=a[i+k]-a[i];
                ans=(a[i+k]+a[i])/2;
            }
        }
        cout<<ans<<"\n";
    }
    return 0;
}

D. Array Splitting (前缀和)

在这里插入图片描述
链接:https://codeforces.com/contest/1175/problem/D

题意: 给定长度为 n n n 个数组,划分成 k k k 个非空子集,要求 ∑ i = 1 n a i × f ( i ) \sum_{i=1}^n a_i\times f(i) i=1nai×f(i)最大。 f ( i ) f(i) f(i) 表示第 i i i 个元素在第 f ( i ) f(i) f(i) 个子集。 ( 1 ≤ k ≤ n ≤ 3 × 1 0 5 , 1 ≤ ∣ a i ∣ ≤ 1 0 6 ) (1\le k \le n \le 3\times 10^5,1\le |a_i| \le 10^6) (1kn3×105,1ai106)

思路:画个简单的示意图就会发现,其实,所有 元素的和 + k − 1 k - 1 k1 个最大后缀和。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+5;
int n,k;
ll a[maxn],ans;
int main()
{
    cin>>n>>k;
    for(int i=1; i<=n; ++i) cin>>a[i];
    for(int i=n; i>=1; --i) a[i]=a[i]+a[i+1];
    ll ans=a[1];
    sort(a+2,a+1+n);
    for(int i=n; i>=n-k+2; --i) ans+=a[i];
    cout<<ans<<"\n";
    return 0;
}
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10;
const ll lim=(1ll<<32)-1;
int n,k;
int a[maxn];
ll pref[maxn];

int main()
{
    ll sum=0;
    cin>>n>>k;
    for(int i=1;i<=n;++i) cin>>a[i];
    for(int i=1;i<=n;++i)
    {
        pref[i]=pref[i-1]+a[i];
        sum+=a[i];
    }

    sort(pref+1,pref+n);
    ll ans=sum*k;

    for(int i=1;i<=k-1;++i)
    {
        ans-=pref[i];
    }
    cout<<ans<<"\n";
    return 0;
}

E. Minimal Segment Cover(倍增)

在这里插入图片描述
题意:给定 n 条线段,给出 m 个区间的询问,问最少需要多少条线段可以覆盖当前询问的区间。 ( n , m ≤ 2 e 5 , l i , r i ≤ 5 e 5 ) (n,m\le 2e5 , l_i,r_i\le 5e5) (n,m2e5,li,ri5e5)

思路

  • d p [ i ] [ j ] dp[i][j] dp[i][j] 表示当前位置 i 通过 2 j 2^j 2j 条线段能够到达的最远距离
  • 预处理出,当前位置 i 通过 1条线段能够到达的最远距离,通过2条线段能够到达的最远距离,通过4条线段能够到达的最远距离。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e5;

int n,m,maxr;
int dp[maxn+10][25];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        dp[l][0]=max(dp[l][0],r);
        maxr=max(maxr,r);
    }
    for(int l=1;l<=maxr;++l)
        dp[l][0]=max(dp[l][0],max(l,dp[l-1][0]));
    for(int j=1;j<=20;++j)
        for(int i=0;i<=maxr;++i)
            dp[i][j]=dp[dp[i][j-1]][j-1];
    while(m--)
    {
        int l,r;
        ll ans=0;
        scanf("%d%d",&l,&r);
        if(r>maxr||dp[l][20]<r)
        {
            puts("-1");
            continue;
        }
        for(int i=20;i>=0;--i)
            if(dp[l][i]<r)
                l=dp[l][i],ans+=(1<<i);
        ans++;
        printf("%lld\n",ans);
    }
    return 0;
}

F. The Number of Subpermutations(思维 + rmq || 分治)

在这里插入图片描述
题意:给以一个长度为 n 的数组,求区间 [ l , r ] [l,r] [l,r] 是排列的个数
思路

  • 排列需要满足,区间长度等于最大值,且每个数只出现一次。
  • 因此处理出每个点 i 向右最多的不同的数字个数。然后就可以枚举每个点,在 i 和 r[i]中找可能的答案

实现

  • 从 n 到 1,记录每个点 i 上次出现的位置,与点 i + 1 取个min
  • 用 rmq 处理一下最大值的查询
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10;

int n;
int a[maxn],last[maxn];
int dp[maxn][20],r[maxn];

void init()
{
    for(int i=1;i<=n;++i) dp[i][0]=a[i];
    for(int j=1;(1<<j)<=n;++j)
        for(int i=1;i+(1<<j)-1<=n;++i)
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
}

int queryMax(int l,int r)
{
    int k=log2(r-l+1);
    return max(dp[l][k],dp[r-(1<<k)+1][k]);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    r[n+1]=n;
    for(int i=n;i>=1;--i)
    {
        if(last[a[i]]) r[i]=min(r[i+1],last[a[i]]-1);
        else r[i]=r[i+1];
        last[a[i]]=i;
    }
    init();
    int ans=0;
    for(int i=1;i<=n;++i)
    {
        int j=i;
        while(j<=r[i])
        {
            if(queryMax(i,j)==j-i+1) ans++,j++;
            else j=i+queryMax(i,j)-1;
        }
    }
    printf("%d\n",ans);
    return 0;
}

学长的分治代码

#include <bits/stdc++.h>
using namespace std;
#define MAXNUM 333333
#define rep(i,s,t) for(int i=s;i<t;i++)
#define pii pair<int,int>
int per[MAXNUM],pos[MAXNUM],f[20][MAXNUM],LOG[MAXNUM],n;
pii fnum[20][MAXNUM],num[MAXNUM];
void getlist()
{
    LOG[0] = -1;
    for (int i = 1; i <= n; i++)
        LOG[i] = LOG[i / 2] + 1;
}
template<typename T> void create(T f[][MAXNUM],T num[])
{
    for (int j = 1; j <= n; j++)
        f[0][j] = num[j];
    for (int i = 1; i <= 20; i++)
        for (int j = 1; j + (1 << i) - 1 <= n; j++)
            f[i][j] = max(f[i - 1][j], f[i - 1][j + (1 << (i - 1))]);
}
template<typename T> T query(T f[][MAXNUM],int qleft, int qright)
{
    int nLog = LOG[qright - qleft + 1];
    return max(f[nLog][qleft], f[nLog][qright - (1 << nLog) + 1]);
}
int res;
void solve(int l,int r)
{
    if(l>r)return;
    pii p=query(fnum,l,r);
    int mid=(l+r)/2;
    if(p.second<=mid)
    {
        rep(i,l,p.second+1)
            if(i+p.first-1<=r&&i+p.first-1>=p.second&&query(f,i,i+p.first-1)<i)
                res++;
    }
    else{
        rep(i,p.second,r+1)
            if(i-p.first+1>=l&&i-p.first+1<=p.second&&query(f,i-p.first+1,i)<i-p.first+1)
                res++;
    }
    solve(l,p.second-1),solve(p.second+1,r);
}
int main()
{
    scanf("%d",&n);
    getlist();
    rep(i,1,n+1)scanf("%d",&num[i].first),num[i].second=i;
    rep(i,1,n+1)
        per[i]=pos[num[i].first],pos[num[i].first]=i;
    create(f,per),create(fnum,num);
    solve(1,n);printf("%d\n",res);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值