Educational Codeforces Round 90 (Div. 2) / contest 1373

目录

        • A Donut Shops
        • B 01 Game
        • C Pluses and Minuses
        • D Maximum Sum on Even Positions
        • E Sum of Digits
        • F Network Coverage
        • G


A B C D E F G

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


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



A Donut Shops

题意

思路

代码

#include 
#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--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
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 a=read(),b=read(),c=read();
        printf("%d ",a<c?1:-1);
        printf("%d\n",c<a*b?b:-1);
    }

    return 0;
}



B 01 Game

题意

思路

代码

#include 
#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--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
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();
    char s[105];
    while(T--)
    {
        scanf("%s",s);
        int n0=0,n1=0;
        for(int i=0;s[i];i++) n0+=s[i]=='0',n1+=s[i]=='1';
        int x=min(n0,n1);
        puts(x&1?"DA":"NET");
    }

    return 0;
}



C Pluses and Minuses

题意:(经过简化后的题意)给出一个 ‘+’ ‘-’ 序列,从前往后遍历,,每遇到 + 就 +1,- 就 -1。初始值从 0 到 inf 遍历,然后对于每个初始值遍历一遍整个序列,如果到了某一次之后这个值变成了负数,就 conintue(初始值++)。输出总共访问这个序列的次数。

思路:先求一个序列的前缀和 s,显然我们对于每个初始值 x,要找的是最先的 s[i]<-x 的位置,如果没有这样的位置那么就结束了(要注意最后一次实际上访问了整个序列,而之前的访问了那个位置之前的序列)。所以一开始遍历 s 计算数组 fi,其意义为 fi[i] 表示 s[d]<-i 成立的 d 的最小值,然后后面遍历初始值求和就可以了。

代码

#include 
#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--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
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=1e6+5;
int s[maxn],fi[maxn],n;
char t[maxn];

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        scanf("%s",t+1);
        n=strlen(t+1);
        REP(i,0,n+3) s[i]=fi[i]=0;
        REP(i,1,n) s[i]=t[i]=='+'?1:-1;
        REP(i,1,n) s[i]+=s[i-1];
        REP(i,1,n) if(s[i]<0 && !fi[-s[i]-1]) fi[-s[i]-1]=i;
        LL ans=0;
        REP(i,0,n)
            if(fi[i]) ans+=fi[i];
            else break;
        printf("%lld\n",ans+n);
    }

    return 0;
}



D Maximum Sum on Even Positions

题意:一个下标从 0 开始的数组,可以进行一次操作,翻转其中任意一个子串(连续的),使得这个数组偶下标的和最大(a[0]+a[2]+a[4]+…),求出这个最大值。

思路:进行几次试验就能发现,如果翻转长度为奇数,那么不会有任何影响;如果翻转长度为偶数,那么对于某个翻转区间,相当于对于每个偶下标的元素,用它们后面或者前面的元素去替换它们,比如说对于 a0,a1,a2,a3,a4,a5,a5,我们可以选择一段连续的偶下标区间,比如说 a2,a4,然后用它们前面后者后面的元素去替换它们(也就是可以用 a1,a3 或者 a3,a5 去替换),那么问题就变为:分别处理用前面替换和后面替换的,对于其中一种,把所有替换方案所带来的增益(可能是负数)按照原来顺序存起来,然后找出一个最大的连续和(可以计算前缀和然后算最大的差),用这个和去加在原来答案上就行了。

代码

#include 
#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--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
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=2e5+5;
int n,a[maxn];
LL b[maxn];

LL cal(VI x)
{
    if(!x.size()) return 0;
    int m=x.size();
    b[0]=x[0];
    REP(i,1,m-1) b[i]=b[i-1]+x[i];
    LL ret=0,minx=0;
    REP(i,0,m-1)
    {
        ret=max(ret,b[i]-minx);
        minx=min(minx,b[i]);
    }
    return ret;
}

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        n=read();
        REP(i,0,n-1) a[i]=read();
        LL ans=0,q=0;
        REP(i,0,n-1) if(!(i&1)) ans+=a[i];
        // left
        VI f;
        for(int i=2;i<n;i+=2) f.pb(a[i-1]-a[i]);
        q=max(q,cal(f));
        // right
        f.clear();
        for(int i=0;i+1<n;i+=2) f.pb(a[i+1]-a[i]);
        q=max(q,cal(f));

        printf("%lld\n",ans+q);
    }

    return 0;
}



E Sum of Digits

题意:设 f(x) 表示 x 的数位和,给出 n 和 k( 1 ≤ n ≤ 150 ,   0 ≤ k ≤ 9 1\le n\le150, \ 0\le k\le 9 1n150, 0k9),找出最小的 x x x ,使得 f ( x ) + f ( x + 1 ) + . . . + f ( x + k ) = n f(x)+f(x+1)+...+f(x+k)=n f(x)+f(x+1)+...+f(x+k)=n

思路:乍一看好像这个数很大,但是如果考虑 k ≥ 2 k\geq 2 k2 的情况,可以发现,平均下来每个数数位和最多是 50,我就草率的认为这个数最大就是 3000000,然后就可以一开始预处理出所有 k ≥ 2 k\geq 2 k2 的答案。

然后对于 k = 0 k=0 k=0 的情况,显然要尽可能多地取 9,然后第一位取剩下的余数;

对于 k = 1 k=1 k=1 的情况,有点复杂:如果 n 为奇数,那么最优的情况一定是 xxxx8,xxxx9,其中第一位取最后的余数,然后其它的都是 9;如果 n 为偶数,经过周密思考我发现,只有可能是 xxxx9,xxxx0 这种情况,再进一步,最优的情况一定是 xxx89,xxx90,然后第一位取最后的余数,其它的都是 9 。

比赛的时候这一题搞了好久,写得也很乱。

代码

#include 
#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--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
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;
}

LL solve0(int n)
{
    int n9=0;
    while(n>=9) n-=9,n9++;
    LL ret=0;
    if(n) ret=n;
    while(n9--) ret=ret*10+9;
    return ret;
}

LL solve1(int n)
{
    if(n%2==0)
    {
        if(n<10) return -1;
        n-=9;
        LL ret=0;
        if(n<=17)
        {
            ret+=n/2;
            ret=ret*10+9;
            return ret;
        }
        n-=17;
        int n9=n/18;
        n-=n9*18;
        ret=n/2;
        while(n9--) ret=ret*10+9;
        ret=ret*10+8;
        ret=ret*10+9;
        return ret;
    }
    int n9=n/18;
    if(!n9) return n/2;
    else
    {
        n-=n9*18;
        int x=n/2+1;
        LL ret=x;
        n9--;
        while(n9--) ret=ret*10+9;
        ret=ret*10+8;
        return ret;
    }
}

int F(LL x)
{
    int ret=0;
    while(x) ret+=x%10,x/=10;
    return ret;
}

int N=3000000;
LL f[10000020];
int ans[155][10];

int main()
{
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    REP(i,1,N+9) f[i]=F(i)+f[i-1];
    REP(k,0,9) REP(i,0,150) ans[i][k]=-1;
    REP(k,2,9) REP(i,k,N+9)
    {
        int q=i==k?f[i]-f[0]:f[i]-f[i-k-1];
        if(q<=150 && ans[q][k]<0) ans[q][k]=i-k;
    }
    //REP(i,1,150) REP(k,2,9) cout<

    int T=read();
    while(T--)
    {
        int n=read(),k=read();
        if(k==0) printf("%lld\n",solve0(n));
        else if(k==1) printf("%lld\n",solve1(n));
        else printf("%d\n",ans[n][k]);
    }

    return 0;
}



F Network Coverage

题意:n 个需求点围成一个圆,每个点需要 a[i] ,每一组相邻的需求点之间有一个供应点,可以提供 b[i]。供应点只能给相邻的点提供供应(且过程中不能为负数,个人理解),问是否存在供应方案。

思路

  • (比赛时的思路,不过没写完)首先如果有一个需求点大于两边的供应点之和,那么肯定不存在;其次如果有一个供应点大于等于两边的需求点之和,那么可以从这里打开这条链,然后遍历一次就可以判断是否存在;所以问题就变为对于剩下情况的求解:假设 1 号供应点给 1 号需求点的值为 x,那么可以根据这个值计算出后面所有供应的值,然后就有好多不等式,然后看 x 能否取到可行解就行了(不知道这个思路对不对)。

  • (以上思路不对,补题后的思路)事实上存在一个单调性,假设 b1 给 a1 的值为 x,那么通过贪心的往后推,可以最终推出 bn 给 a1 的值 y,并且在可行条件下(也就是中间不存在不可行的分配),x 越大,y越小,而且在 y ≥ 0 y\ge 0 y0 的条件下,x 越大越好,因为 x 每加一,y 最多减一,所以 x 越大越好。所以我们的目标就是通过二分搜索,找到在 y ≥ 0 y\ge 0 y0 的条件下最大的 x,然后判断是否满足 a1 就行了。

  • (另外一个思路)先补一个赫尔婚姻定理

设二分图 G={X+Y, M} ,使得 X 完全匹配的充要条件是 ∀ M ⊆ X ,   ∣ M ∣ ≤ N G ( x ) \forall M\subseteq X, \ |M|\le N_G(x) MX, MNG(x) 。也就是说对于 X 中任意的点集,X 的大小一定不大于与这个点集相连的所有结点的数目(否则相当于很多人抢很少的东西)。这个必要性很好看出来,充分性就不说了(其实是不会)。

​ 那么对于这道题,其实可以看成一个二分图,要求对于任意个 a,与其相连的 b 的和要大于等于这些 a 的和,那么如何优雅地表示任意个 a 呢?其实只需要保证任意连续多个 a 成立就行了,因为连续一定比不连续更优(不连续说明 b 更多)。那么问题转化为:设 A 是 a 的前缀和,B 是 b 的前缀和,如果存在 A[r] - A[l] > B[r] - B[l-1] 就说明不可行;于是就可以预处理出 A[r]-B[r] 和 A[l]-B[l-1] 这两个数组,然后遍历一遍就可以了。还要注意因为是一个圈,所以要加倍展开;以及要特判全部的 a 和全部的 b 的比较,因为以上计算时 b 都要比 a 多一个。

代码

  • (二分)
#include 
#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--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
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=1e6+5;
LL a[maxn],b[maxn];
int n;

LL get(LL x)
{
    LL xx=x;
    REP(i,2,n)
    {
        LL y=b[i-1]-x;
        LL z=y>=a[i]?0:a[i]-y;
        if(z>b[i]) return -1;
        x=z;
    }
    return b[n]-x;
}

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        n=read();
        REP(i,1,n) a[i]=read();
        REP(i,1,n) b[i]=read();
        LL l=0,r=b[1]+1,mid;
        while(l<r-1)
        {
            mid=(l+r)>>1;
            if(get(mid)>=0) l=mid;
            else r=mid;
        }
        puts(l+get(l)>=a[1]?"YES":"NO");
    }

    return 0;
}
  • (赫尔婚姻定理)
#include 
#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--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
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=3e6+5;
LL a[maxn],b[maxn],A[maxn],B[maxn];

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        int n=read(); int nn=n;
        REP(i,3,n+2) a[i]=read();
        REP(i,3,n+2) b[i]=read();
        a[1]=a[n+1]; a[2]=a[n+2];
        b[1]=b[n+1]; b[2]=b[n+2];
        REP(i,n+3,2*n+2) a[i]=a[i-n],b[i]=b[i-n];
        n=2*n+2;
        REP(i,1,n) a[i]+=a[i-1],b[i]+=b[i-1];
        REP(i,1,n) A[i]=a[i]-b[i],B[i]=a[i]-b[i-1];
        if(a[n]-a[n-nn]>b[n]-b[n-nn]) {puts("NO"); continue;}
        int flag=1;
        LL minx=1e18;
        REP(i,1,n)
        {
            if(A[i]>minx) {flag=0; break;}
            minx=min(minx,B[i]);
        }
        puts(flag?"YES":"NO");
    }

    return 0;
}



G

题意

思路

代码


你可能感兴趣的:(codeforces)