Educational Codeforces Round 135

A

题意

一个袋子有n种颜色的球,每种若干个,每次操作可以取出两个不同颜色的球,问最后留下的球可能是什么颜色。

思路

很显然,最后留下的是数量最多的那种颜色。因为可以每次选数量第二多的颜色和其他任意一个颜色取出。直到只剩一种颜色,而此时剩下的那种颜色数量不可能大于原来数量最多的颜色,两两匹配最后一定留下数量最多的那种颜色。

代码

#include<bits/stdc++.h>

using namespace std;

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,x,maxx=-1,idx;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            if(x>maxx)
            {
                idx=i+1;
                maxx=x;
            }
        }
        printf("%d\n",idx);
    }
}

B

题意

按要求构造一个长为n的排列。给一个变量x,初始为零,从左到右遍历这个排列。

  • 对于某一个pi,若x < pi,则x += pi 。否则x0

要求构造一个排列,使得最后x的值最大。

思路

可以观察到,要使得x一直增长,那么这种排列一定有

\[\sum_{i=1}^{n-1}a_i<a_n \]

那么最后一位对答案的贡献一定大于前面所有位,那么只需要最后一位选n,前一位选n-1。然后注意一下构造规则就好了。

代码

#include<bits/stdc++.h>

using namespace std;

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        if(n&1)
        {
            printf("1 %d ",n-2);
            for(int i=n-3;i>=2;i--)
                printf("%d ",i);
            printf("%d %d\n",n-1,n);
        }
        else
        {
            for(int i=n-2;i>=1;i--)
                printf("%d ",i);
            printf("%d %d\n",n-1,n);
        }
    }
}

C

题意

给两个数组a,b。一种操作,把某个数x变为x的数位长度,如1230变为4431变为3。求最少的操作,使得a,b中所含元素相同(位置不一定相同)。

思路

对于a数组中某一个数x,如果b数组中有相等的数,那么可以直接匹配,如果没有,那么或者需要用操作改变x,或者需要由b中的某个数y经过操作后变为x

显然操作只能减小x,那么如果有比x更大的数,x有可能不需要操作,若没有,那么x肯定需要操作。所以可以想到从大到小考虑,每次取a,b中各自最大数x,y。若x==y,那么可以直接匹配,不需要操作,如果存在x<y或者y>x,那么就对较大的数进行一次操作再重新选择两边最大的数。可以使用大顶堆很方便的实现。

代码

#include<bits/stdc++.h>

using namespace std;
const int MAX=2e5+5;

int a[MAX],b[MAX],cnt[15];

priority_queue<int> Qa,Qb;

int len(int x)
{
    int res=0;
    while(x)
    {
        res++;
        x/=10;
    }
    return res;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,x,res=0;
        memset(cnt,0,sizeof cnt);
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]),Qa.push(a[i]);
        for(int i=0;i<n;i++)
            scanf("%d",&b[i]),Qb.push(b[i]);
        while(!Qa.empty())
        {
            if(Qa.top()==Qb.top())
            {
                Qa.pop();
                Qb.pop();
                continue;
            }
            res++;
            if(Qa.top()>Qb.top())
            {
                Qa.push(len(Qa.top()));
                Qa.pop();
            }
            else
            {
                Qb.push(len(Qb.top()));
                Qb.pop();
            }
        }
        printf("%d\n",res);
    }
}

D

题意

给一个字符串S,长为偶数。AB二人博弈,轮流操作,每次可以取走S最前或最后的字符,插入各自的字符串中。最后自己的字符串字典序较小的人获胜。求最终谁获胜。

思路

博弈论,可以画一下博弈图。

可以看到,这个游戏是先手必定不败的,因为每次先手都可以确定一个更优的值,并且当只剩两个元素时,先手的人总可以选择一个更优的值,所以一定先手必定不败。

对于某一个串abcd,一次轮流博弈可以转移到三个状态[ab]cd,[a]bc[d],ab[cd][]表示被选择掉的字符。定义某一个状态dp["abcd"]1表示此状态先手必胜,为0则表示先手必定平局。

因为最后比较字典序,所以博弈过程中的选择也会影响两人胜负,所以每次要考虑两人选择字符是否一直,所以每次转移考虑一次轮流博弈,按照博弈图进行状态转移。

如果A首先选了a,那么B可以选择b,d,如果A首先选择d,那么B可以选择a,c,对于转移得到的三个状态,如果无论A先选a还是dB都能找到c或者b使得二人选择的字符相等,并且子状态也是0,那么当前为先手必平,否则为先手必胜。

最后用记忆化搜索实现一下比较好写。

代码

#include<bits/stdc++.h>

using namespace std;
const int MAX=2e3+3;

char s[MAX];
int dp[MAX][MAX];

bool dfs(int l,int r)
{
    if(dp[l][r]!=-1)return dp[l][r];
    if(r-l==1)return dp[l][r]=(s[l]!=s[r]);
    bool LL=!dfs(l,r-2),MM=!dfs(l+1,r-1),RR=!dfs(l+2,r);
    if(MM&&s[l]==s[r])return dp[l][r]=0;
    if((LL&&s[r]==s[r-1])&&(RR&&s[l]==s[l+1]))return dp[l][r]=0;
    return dp[l][r]=1;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(dp,-1,sizeof dp);
        scanf("%s",s);
        int n=strlen(s);
        if(dfs(0,n-1))
            printf("Alice\n");
        else
            printf("Draw\n");
    }
}

E

题意

n个盘子,红色,黑色两种调料,每个盘子里面可以任选一种调料加入,提高ai,bi的美味度。求最高的美味度之和。除此之外,限制调料只能整包购买,一整包红色调料有x个红色调料,一整包黑色调料有y个黑色调料,有q个询问,每个询问给定xi,yi,分别表示一整包红色,一种包黑色包含的各自颜色调料的数量。对每个询问,输出在这种条件下的最高美味度。

思路

显然可以得到,每次询问对应一个方程(r表示购买r整包红色,b表示购买b整包黑色。)

\[x_i*r+y_i*b=n \qquad(1) \]

然后很容易想到,最优解应该是ai>bi时选红色,ai<bi时选黑色,那么就要找到符合方程(1)限制的最接近最优解的答案就是最高美味度。

所以想到可以先全部选黑色,然后逐渐把红色贡献更大的盘子换成红色,不难想到这个替换过程是一个单峰函数。

同时,方程(1)的解可以有以下公式推导。

\[g=gcd(x_i,y_i)\\ \begin{eqnarray} 方程(1) \begin{cases} 有解&,n\mod g=0\\ 无解&,n\mod g\neq0 \end{cases} \end{eqnarray}\\ \\ x_i*r+y_i*b=g\qquad(2)\\\\ (2)有特解r_0,b_0\\ 则(1)有通解\\ r1=\frac{r_0*n}{g}+k*\frac{y_i}{g}\\ b1=\frac{b_0*n}{g}-k*\frac{x_i}{g}\\ k为任意常数 \]

按照题意,只需要保留非负整数解,所以有

\[r1=\frac{r_0*n}{g}+k*\frac{y_i}{g}\geq0\\ \Rightarrow k\geq \lceil-\frac{r_0*n}{y_i} \rceil \\ b1=\frac{b_0*n}{g}-k*\frac{x_i}{g}\\ \Rightarrow k\leq \lfloor\frac{b_0*n}{x_i} \rfloor \\ \Rightarrow \lceil-\frac{r_0*n}{y_i} \rceil\leq k\leq \lfloor\frac{b_0*n}{x_i} \rfloor \\ \]

可以想到k在范围内变化的过程就是一个单峰函数,所以可以用三分法在O(logn)时间内处理得到一个查询的最优解。

代码实现用扩展欧几里得求方程的解,三分求最大值。

#include<bits/stdc++.h>

using namespace std;
using ll=long long;

const int MAX=3e5+5;

ll d[MAX],pre[MAX];
int n,q,a,b;
ll x,y,g,sum=0;

ll exgcd(ll a,ll b,ll& x,ll& y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    ll d=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
    return d;
}

inline ll f(int k)
{
    ll red=(n/g*x+b/g*k)*a;
    ll cur=sum;
    cur-=pre[red];
    return cur;
}

ll sanfen_integer(int l,int r)
{
    ll res=max(f(l),f(r));
    while(l<r-1)
    {
        int mid=(l+r)>>1;
        int mmid=(mid+r)>>1;
        if(f(mid)>f(mmid))
            res=max(res,f(mid)),r=mmid;
        else
            res=max(res,f(mmid)),l=mid;
    }
    res=max(res,max(f(l),f(r)));
    return res;
}

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        d[i]=b-a;
        sum+=b;
    }
    sort(d,d+n);
    for(int i=0;i<n;i++)
        pre[i+1]=pre[i]+d[i];
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d",&a,&b);
        g=exgcd(a,b,x,y);
        if(n%g)
        {
            printf("-1\n");
            continue;
        }
        ll l=ceil(double(n)/double(b)*(-x));
        ll r=floor(double(n)/double(a)*y);
        if(l>r)
        {
            printf("-1\n");
            continue;
        }
        ll res=sanfen_integer(l,r);
        printf("%I64d\n",res);
    }
}
posted @ 2022-09-17 23:19  cryingrain  阅读(36)  评论(0编辑  收藏  举报