Codeforces Round #645 (Div. 2) / contest 1358


A B C D E F

( √:做出; ●:尝试未做出; ○:已补题 )


题目地址:https://codeforces.com/contest/1358

这次是疫情主题。




A Park Lighting

题意

思路

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    int t=read();
    while(t--)
    {
        int n=read(),m=read();
        if(n>m) swap(n,m);
        if(m%2==0) printf("%d\n",m/2*n);
        else printf("%d\n",m/2*n+(n+1)/2);
    }
 
    return 0;
}



B Maria Breaks the Self-isolation

题意

思路

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=1e5+5;
int t,n,a[maxn];

int main()
{
    //freopen("input.txt","r",stdin);
    t=read();
    while(t--)
    {
        n=read();
        REP(i,1,n) a[i]=read();
        sort(a+1,a+n+1);
        int u=0;
        REP(i,1,n) if(a[i]<=i) u=i;
        printf("%d\n",u+1);
    }

    return 0;
}



C Celex Update

题意

思路:可以发现答案仅跟给出的长方形的形状有关,和位置无关,就很容易找出规律。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        LL x1=read(),y1=read(),x2=read(),y2=read();
        LL n=abs(x2-x1),m=abs(y2-y1);
        if(n>m) swap(n,m);
        LL ans=n*(n-1)+n*(m-n+1);
        printf("%lld\n",ans+1);
    }

    return 0;
}



D The Best Vacation

题意:一年中有 n 个月,每个月有 d i d_i 天,在每个月的 c 号出行的收益为 c,可以连续出行 x 天,问最大收益为多少。

思路:如果不考虑月份边界,那么出行时间往后挪,直到左边界或者右边界碰到了月份边界,收益都是单调变化的,所以可见出行时间的左右边界在月份边界的时候是分割点,所以只用在这个时候更新答案即可。

当时比赛的时候想得不是很清楚,导致代码异常复杂,东拼西凑,勉强过了。

题解给出的方法是,最优出行的右边界一定是月份的末尾,否则的话,向左平移一位一定会导致答案更差(其实更一开始分析的差不多,因为是单调的),所以只用枚举右边界,左边界用二分查找就可以了。这样的复杂度是 O(nlogn),我这种是 O(n)(但是写得太复杂了…)

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=4e5+5;
int n,d[maxn];
LL x,s;

LL sum(int i,int n)
{
    return 1ll*i*n+1ll*n*(n-1)/2;
}

int main()
{
    //freopen("input.txt","r",stdin);
    cin>>n>>x;
    int ld=1,l=1,rd,r;
    REP(i,1,n) d[i]=read();
    REP(i,1,n)
    {
        if(s+d[i]<x) s+=d[i];
        else {rd=i,r=(int)(x-s); break;}
    }
    int rrd=rd,rr=r;
    REP(i,n+1,n*2) d[i]=d[i-n];
    n<<=1;
    LL up=0,maxx=0;
    if(x==1)
    {
        REP(i,1,n) if(d[i]>maxx) maxx=d[i];
        return printf("%lld\n",maxx),0;
    }
    while(1)
    {
        //cout<<up<<endl;
        //cout<<ld<<' '<<l<<' '<<rd<<' '<<r<<endl;
        if(rd>n) break;
        if(ld==rd)
        {
            up+=sum(d[ld]-r+l,r-l+1)-sum(l,r-l+1);
            maxx=max(maxx,up);
            up-=d[ld]-r+l;
            up+=1;
            l=d[ld]-r+l+1;
            r=1; rd++;
        }
        else
        {
            if(d[ld]-l==d[rd]-r)
            {
                up-=sum(l,d[ld]-l+1);
                up+=sum(r+1,d[rd]-r)+1;
                maxx=max(maxx,up-1+d[ld]);
                ld++, rd++, l=r=1;
            }
            else if(d[ld]-l<d[rd]-r)
            {
                up-=sum(l,d[ld]-l+1);
                up+=sum(r+1,d[ld]-l+1);
                maxx=max(maxx,up);
                r+=d[ld]-l+1;
                ld++, l=1;
            }
            else
            {
                up-=sum(l,d[rd]-r+1);
                up+=sum(r+1,d[rd]-r)+1;
                maxx=max(maxx,up-1+l+d[rd]-r);
                l+=d[rd]-r+1;
                rd++, r=1;
            }
        }
    }
    //cout<<maxx;
    LL ans=0;
    REP(i,1,rrd-1) ans+=sum(1,d[i]);
    ans+=sum(1,rr)+maxx;
    cout<<ans;

    return 0;
}



E Are You Fired?

题意:一个数列长度为 n,后面 n 2 \lfloor \frac{n}{2}\rfloor 的所有元素都相同,问是否存在一个 k,使得这个数列所有长为 k 的连续子列的和都严格大于 0 。

思路:比赛的时候一直在想,这个元素相同这个条件究竟有什么作用,我推测出这个条件暗示 k 会大于等于 n/2,不过后面就没思路了。

我的猜测是正确的。假设 s i = a i + . . . + a i + k 1 s_i=a_i+...+a_{i+k-1} ,如果存在一个 k 可行,那么把 k 翻倍之后,因为 s i = s i + s i + k > 0 s_i^{'}=s_i+s_{i+k}>0 ,所以也成立。注意到 s i + 1 = s i + ( a i + k a i ) = s i + x a i s_{i+1}=s_i+(a_{i+k}-a_i)=s_i+x-a_i ,可以推出 s i + 1 = s 1 + i x j = 1 i a j s_{i+1}=s_1+i\cdot x-\sum\limits_{j=1}^i a_j ,其中设 b i = i x j = 1 i a j b_i=i\cdot x-\sum\limits_{j=1}^i a_j ,则 s i + 1 = s 1 + b i s_{i+1}=s_1+b_i ,可以发现 b i b_i 的取值跟 k 没有关系,所以看某个 k 是否可行,就是看 min 0 i n k { s 1 + b i } > 0 \min\limits_{0\le i\le n-k} \{s_1+b_i\}>0 是否成立。所以一开始先维护一个 b 的前缀最小值,然后枚举 k 判断即可。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=5e5+5;
LL a[maxn],b[maxn],x,s,s1;

int main()
{
    //freopen("input.txt","r",stdin);
    int n=read(),ans=0;
    REP(i,1,(n+1)/2) a[i]=read();
    x=read();
    REP(i,(n+1)/2+1,n) a[i]=x;
    REP(i,1,n)
    {
        s+=a[i];
        b[i]=min(b[i-1],x*i-s);
    }
    REP(i,1,n/2) s1+=a[i];
    REP(k,n/2+1,n)
    {
        s1+=a[k];
        if(s1+b[n-k]>0)
        {
            ans=k;
            break;
        }
    }
    printf("%d\n",ans?ans:-1);

    return 0;
}



F Tasty Cookie

题意:两个长度为 n( 1 n 2 e 5 1\le n \le 2e5 )的数组 A 和 B( 1 A i , B i 1 0 12 1 \le A_i,B_i\le 10^{12} ),可以对 A 进行两种操作:1.翻转;2.求前缀和。问能否通过若干次操作,使得 A 变为 B,如果可以并且操作次数小于等于 2e5,还要输出操作方案。

思路:比赛的时候根本没看这道题。

首先根据数据限制,我们可以思考最多能求多少次前缀和,然后发现当 n 2 n\le 2 的时候次数可以非常多,当 n > 2 n>2 时就可以暴力处理,因为翻转一定不会比求前缀和多多少。

如果 n = 1 n=1 ,那就是一个判断是否相等的问题;

否则,我们考察一个前缀数组,在题目数据限制下,这个前缀数组一定是严格单增的,所以可以用 B 数组反推回去:

  • 如果 n = 2 n=2 ,这里可以通过取模运算加速第二种操作,这种情况要注意很多细节问题;

  • 如果 n > 2 n>2 ,那么就循环处理,每次判断是否和 A 或者 A 的翻转相等,不相等的话就判断是否单增或者单减,如果单调则继续处理(差分),否则就 IMPOSSIBLE。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const LL maxn=2e5;
int n,tot;
LL a[maxn+5],b[maxn+5];
char s[maxn*10];

bool equalzheng()
{
    REP(i,1,n) if(a[i]!=b[i]) return 0;
    return 1;
}

bool equalfan()
{
    REP(i,1,n) if(a[i]!=b[n-i+1]) return 0;
    return 1;
}

bool danzeng()
{
    REP(i,1,n-1) if(b[i+1]<=b[i]) return 0;
    return 1;
}

bool danjian()
{
    REP(i,1,n-1) if(b[i+1]>=b[i]) return 0;
    return 1;
}

void backb()
{
    REP_(i,n,2) b[i]-=b[i-1];
}

int main()
{
    //freopen("input.txt","r",stdin);
    n=read();
    REP(i,1,n) scanf("%lld",&a[i]);
    REP(i,1,n) scanf("%lld",&b[i]);
    if(n==1)
    {
        if(a[1]==b[1]) printf("SMALL\n0");
        else puts("IMPOSSIBLE");
    }
    else if(n==2)
    {
        LL ans=0;
        while(!equalzheng())
        {
            if(b[1]>b[2])
            {
                swap(b[1],b[2]);
                if(ans<maxn) s[++tot]='R';
            }
            if(equalfan()) {s[++tot]='R'; break;}
            if(b[1]==min(a[1],a[2]))
            {
                if(b[2]<max(a[1],a[2]) || (b[2]-max(a[1],a[2]))%b[1]!=0)
                    return puts("IMPOSSIBLE"),0;
                LL x=(b[2]-max(a[1],a[2]))/b[1];
                ans+=x;
                if(ans<=maxn) while(x--) s[++tot]='P';
                if(b[1]!=a[1]) s[++tot]='R';
                break;
            }
            if(b[1]<a[1] && b[1]<a[2]) return puts("IMPOSSIBLE"),0;
            LL x=(b[2]-b[1])/b[1]+1;
            ans+=x;
            if(ans<=maxn) while(x--) s[++tot]='P';
            b[2]%=b[1];
        }
        if(ans<=maxn)
        {
            printf("SMALL\n%d\n",tot);
            REP_(i,tot,1) putchar(s[i]);
        }
        else printf("BIG\n%lld",ans);
    }
    else
    {
        LL ans=0;
        while(1)
        {
            if(equalzheng()) break;
            if(equalfan()) {if(ans<=maxn) s[++tot]='R'; break;}
            if(danzeng()) {ans++; if(ans<=maxn) s[++tot]='P'; backb();}
            else if(danjian()) {if(ans<=maxn) s[++tot]='R'; reverse(b+1,b+n+1);}
            else return puts("IMPOSSIBLE"),0;
        }
        if(ans<=maxn)
        {
            printf("SMALL\n%d\n",tot);
            REP_(i,tot,1) putchar(s[i]);
        }
        else printf("BIG\n%lld",ans);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/dragonylee/article/details/106399779