Educational Codeforces Round 80

EduRound 80

A

题意

有一项任务,要求在n天内完成,不优化的话需要d天,如果用 x 天来优化则可以变成 d/(x+1) (向下取整)天。即总共花 x+d/(x+1) 天,求是否可以按要求完成。

思路

总时间很明显是个凹函数,考虑三分,但是由于存在向下取整,所以不是严格凹函数,所以不能三分。但是可以直接求导,求导一下得到最优的xsqrt(4d)/2-1

代码

#include<bits/stdc++.h>
using namespace std;
 
int n,d;
 
int f(int x)
{
    return x+d/(x+1)+(d%(x+1)>0);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&d);
        int x=int(sqrt(4.0*d)/2.0-1);
        printf(f(x)<=n?"YES\n":"NO\n");
    }
}

B

题意

给出A,B寻找有多少对a,b, a∈[1,A],b∈[1,B] 使得 ab+a+b=a拼接b 。拼接即两数连接在一起得到新数,如 6拼接9得到69

思路

打表找规律,发现b9,99,998,......a 任选,所以计算一下 [1,B] 有多少 9... 这种数,再乘 A 就是答案。

代码

#include<bits/stdc++.h>
using namespace std;
 
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        long long a,b,k=0,cur=9;
        scanf("%I64d%I64d",&a,&b);
        while(cur<=b)
        {
            k++;
            cur=cur*10+9;
        }
        printf("%I64d\n",a*k);
    }
}

C

题意

给出n,m,求有多少对数组a,b满足:

  • a,b长度均为m
  • a,b中所有元素均∈[1,n](可重复)
  • 所有对应位置元素有ai<=bi
  • a保证单调不降
  • b保证单调不增

思路

分开考虑比较复杂,所以考虑a,b合起来,将a正序排列,b倒序排列接在a后面,则可以得到一个c,使得c满足长度为2m,且单调不降。则问题转化为 [1,n] 中选择 2m 个可重复的数使得构成一个单调不降序列。可以用两种方法解决

  1. DP:状态定义为dp[i][j]表示c中第i位为j的选法个数。因为保持单调不降,所以转移就由前一位小于等于j的状态转移而来。

  2. 组合数学:假设 [1,n]i 个数选择 xi 次,则问题转化为方程

    \[\sum_{i=1}^nx_i=2m \]

    用隔板法解决。因为可选多次,可不选,所以增加n个,答案就是

    \[C_{2m+n-1}^{n-1} \]

代码

DP

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e3+3;
const int mod=1e9+7;
 
int dp[25][MAX];
 
int main()
{
    int n,m,res=0;
    scanf("%d%d",&n,&m);
    for(int i=0; i<n; i++)
        dp[1][i]=1;
    for(int i=2; i<=2*m; i++)
        for(int j=0; j<n; j++)
            for(int k=0; k<=j; k++)
                dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
    for(int i=0;i<n;i++)
        res=(res+dp[2*m][i])%mod;
    printf("%d\n",(res+mod)%mod);
}

排列组合

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const ll mod=1e9+7;
 
ll qpow(ll x,ll k)
{
    ll res=1;
    while(k)
    {
        if(k&1)
            res=res*x%mod;
        k>>=1;
        x=x*x%mod;
    }
    return res;
}
ll inv(ll x){return qpow(x,mod-2);}
ll jc(ll x)
{
    ll res=1;
    while(x){res=res*x%mod;x--;}
    return res;
}
 
int main()
{
    ll n,m,a,b;
    scanf("%I64d%I64d",&n,&m);
    a=2*m+n-1;
    b=n-1;
    n=jc(a);
    m=jc(b)*jc(a-b)%mod;
    printf("%I64d\n",n*inv(m)%mod);
}

D

题意

给出n个数组,长度均为m,可以选两个数组(可以相同),然后对应位置取max得到一个新数组,求如何选择使得最后得到的新数组中的最小值最大。

思路

最小值最大很容易想到二分答案,所以问题就是如何快速判断一个解是否可行。因为m很小,所以容易想到状态压缩。假设当前判断答案x。对于任意一个数组a,如果a[i]>=x,则记此位为1,否则记为0,则所以数组都可以压缩成最多255的数,所以状态总共有255种,可以O(n^2)的判断两个数组是否可行,可行即二者按位或得到全为1

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX=3e5+5;
 
int n,m,ra,rb,a[MAX][10];
int mp[(1<<8)];
 
bool check(int x)
{
    memset(mp,0,sizeof mp);
    for(int i=0; i<n; i++)
    {
        int cur=0;
        for(int j=0; j<m; j++)
            cur|=(a[i][j]>=x)<<(m-j-1);
        mp[cur]=i+1;
    }
    int tot=(1<<m);
    for(int i=0; i<tot; i++)
        for(int j=0; j<tot; j++)
            if(mp[i]&&mp[j]&&((i|j)==tot-1))
            {
                ra=mp[i],rb=mp[j];
                return 1;
            }
    return 0;
}
 
int main()
{
    int L=0,R=0,res;
    scanf("%d%d",&n,&m);
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            scanf("%d",&a[i][j]),R=max(R,a[i][j]);
    while(L<=R)
    {
        int mid=(L+R)>>1;
        if(check(mid))
        {
            res=mid;
            L=mid+1;
        }
        else
            R=mid-1;
    }
    if(ra>rb)swap(ra,rb);
    printf("%d %d\n",ra,rb);
}

E

题意

给一个长度为n的排列,初始为1-nm个操作,每次选一个x,然后把xx原来所在位置p[x]提到第一位,1-p[x]-1的数依次往后推,求1-n在操作过程中位置能达到的最大值和最小值

思路

将题目中操转化为在排列前面预留m个位置,然后每次操作将p[x]置零,然后将最左边的位置改为x,这样就避免了往后推的操作。然后min就是1或者最初的位置,max就是位于前面的数的数量。如果x没有被提到第一位,则min不会变化,max只会增加,所以用树状数组维护,改变了的x在操作过程中更新,没有改变的x在所有操作结束后更新。

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX=3e5+5;
 
int n,m,c[MAX<<1],p[MAX],minn[MAX],maxx[MAX];
 
void add(int x,int k)
{
    for(;x<=n+m;x+=x&-x)
        c[x]+=k;
}
int query(int x)
{
    int sum=0;
    for(;x;x-=x&-x)
        sum+=c[x];
    return sum;
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        p[i]=i+m;
        minn[i]=i;
        maxx[i]=i;
        add(p[i],1);
    }
    for(int i=0;i<m;i++)
    {
        int x;
        scanf("%d",&x);
        maxx[x]=max(maxx[x],query(p[x]));
        minn[x]=1;
        add(p[x],-1);
        p[x]=m-i;
        add(p[x],1);
    }
    for(int i=1;i<=n;i++)
    {
        maxx[i]=max(maxx[i],query(p[i]));
        printf("%d %d\n",minn[i],maxx[i]);
    }
}
posted @ 2020-03-20 22:35  cryingrain  阅读(160)  评论(0编辑  收藏  举报