Educational Codeforces Round 122(Rated for Div. 2)

A-Div. 7

\(t\)组样例,每个样例给你一个正整数\(n\),要求你改变最少数量的数位使得它没有前导\(0\)且能被\(7\)整除。每组输出任何一个可能的答案即可。\((1\leq t\leq990,10\leq n\leq999)\)

思路

只要改个位数应该就可以了吧。

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int _;
    scanf("%d",&_);
    while(_--)
    {
        int n;
        scanf("%d",&n);
        if(n%7==0)printf("%d\n",n);
        else
        {
            int mod=n%7;
            if(n%10>=mod)printf("%d\n",n-mod);
            else printf("%d\n",n+7-mod);
        }
    }
    return 0;
}

B-Minority

\(t\)组样例,每个样例给你一个“\(01\)”串\(s\),你只能选取一个区间,然后删掉个数少的字符。每组问最多能删掉多少字符。\((1\leq t\leq10^4,\mid s\mid\leq2\cdot10^5)\)

思路

考虑全选,然后能删的一定是最多的,但是如果两个一样,那就减掉边上的一个就好了。

代码

#include<bits/stdc++.h>
using namespace std;
char s[1000005];
int main()
{
    int _;
    scanf("%d",&_);
    while(_--)
    {
        scanf("%s",s+1);
        int n=strlen(s+1);
        int num1=0,num0=0;
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='1')num1++;
            else num0++;
        }
        if(num1==num0)printf("%d\n",num1-1);
        else printf("%d\n",min(num1,num0));
    }
    return 0;
}

C-Kill the Monster

\(t\)组样例,每个样例给你\(h_c,d_c,h_m,d_m\),分别代表自己的血量、攻击力和怪兽的血量与攻击力。然后你有\(k\)次加倍的机会,每次加倍你都能选择使自己的攻击力增加\(w\)或者使自己的血量增加\(a\)。每组样例问你能否打败怪兽。\((1\leq t\leq5\cdot10^4)\)\((1\leq h_c\leq10^{15},1\leq d_c\leq10^9,1\leq h_m\leq10^{15},1\leq d_m\leq10^9)\)\((0\leq k\leq2\cdot10^5,0\leq w\leq10^4,0\leq a\leq10^{10})\)

思路

因为能加强,那肯定要么加血要么加攻击力,所以\(for\)一遍\(k\)就能结束了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int _;
    scanf("%d",&_);
    while(_--)
    {
        ll hc,dc,hm,dm;
        scanf("%lld%lld%lld%lld",&hc,&dc,&hm,&dm);
        ll k,w,a;
        scanf("%lld%lld%lld",&k,&w,&a);
        bool ck=0;
        for(int i=0;i<=k;i++)
        {
            if(hm/(dc+i*w)+(bool)(hm%(dc+i*w))<=(hc+(k-i)*a)/dm+(bool)((hc+(k-i)*a)%dm)){ck=1;break;}
        }
        if(ck)printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

D-Make Them Equal

有t组样例,每个样例有两个数组\(b_1,b_2,…,b_n\)\(c_1,c_2,...,c_n\),然后你还有一个数组\(a_1,a_2,...,a_n\),初值为\(1\)。现在你能进行\(k\)次操作,每次操作你能任选两个正整数\(i\)\(x\),然后把\(a_i\)变成\(a_i+⌊\frac{a_i}{x}⌋\),如果\(a_i\)等于\(b_i\),你就能得到值\(c_i\)的硬币。问每组询问最多能得到多少价值硬币。\((1\leq t\leq100,1\leq n\leq10^3,0\leq k\leq10^6,1\leq b_i\leq10^3,1\leq c_i\leq10^6)\)

思路

首先想法当然是背包,但是\(1e9\)当然是冲不过\(1\)秒的,但是这道题很良心的给了两秒。然后来想一想如果只给一秒要怎么做。打个表能发现把\(1\)改成其他数最多只需要\(13\)步,那么当\(k\)大于\(13*n\)时答案就是\(\displaystyle\sum_{i=1}^{n}c_i\)了,反之,就可以来一个背包以\(1e3*1e4\)的复杂度轻松过掉了。

代码1

#include<bits/stdc++.h>
using namespace std;
int b[1005],c[1005];
struct node
{
    int a,s;
};
queue<node>q;
int vis[1005];
int stp[1005];
int dp[1000020];
int main()
{
    q.push({1,0});
    vis[1]=1;
    stp[1]=0;
    while(!q.empty())
    {
        auto p=q.front();
        q.pop();
        int a=p.a,s=p.s;
        for(int i=1;i<=a;i++)
        {
            int x=a+a/i;
            if(x<=1000&&!vis[x])
            {
                q.push({x,s+1});
                vis[x]=1;
                stp[x]=s+1;
            }
        }
    }
    int _;
    scanf("%d",&_);
    while(_--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&b[i]);
            b[i]=stp[b[i]];
        }
        for(int i=1;i<=n;i++)scanf("%d",&c[i]);
        for(int i=1;i<=n;i++)
            for(int j=1000000;j>=0;j--)
                if(dp[j]||j==0)
                    dp[j+b[i]]=max(dp[j+b[i]],dp[j]+c[i]);
        int ans=0;
        for(int i=0;i<=k;i++)ans=max(ans,dp[i]);
        printf("%d\n",ans);
        if(_)
            for(int i=0;i<=1000000;i++)
                    dp[i]=0;
    }
    return 0;
}

代码2

#include<bits/stdc++.h>
using namespace std;
int b[1005],c[1005];
struct node
{
    int a,s;
};
queue<node>q;
int vis[1005];
int stp[1005];
int dp[1005][13020];
int main()
{
    q.push({1,0});
    vis[1]=1;
    stp[1]=0;
    while(!q.empty())
    {
        auto p=q.front();
        q.pop();
        int a=p.a,s=p.s;
        for(int i=1;i<=a;i++)
        {
            int x=a+a/i;
            if(x<=1000&&!vis[x])
            {
                q.push({x,s+1});
                vis[x]=1;
                stp[x]=s+1;
            }
        }
    }
    int _;
    scanf("%d",&_);
    while(_--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&b[i]);
            b[i]=stp[b[i]];
        }
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&c[i]);
            sum+=c[i];
        }
        if(k>13*n){printf("%d\n",sum);continue;}
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=13*n;j++)
            {
                if(dp[i-1][j])
                {
                    dp[i][j]=max(dp[i][j],dp[i-1][j]);
                    dp[i][j+b[i]]=max(dp[i][j+b[i]],dp[i-1][j]+c[i]);
                }
            }
            dp[i][b[i]]=max(dp[i][b[i]],dp[i-1][0]+c[i]);
        }
        int ans=0;
        for(int i=0;i<=k;i++)ans=max(ans,dp[n][i]);
        printf("%d\n",ans);
        if(_)
            for(int i=1;i<=n;i++)
                for(int j=0;j<=13*n;j++)
                    dp[i][j]=0;
    }
    return 0;
}

E-Spanning Tree Queries

给你一个\(n\)个点,\(m\)条边(可能有重边),且有边权的无向图,然后有\(k\)组询问,每次询问会给你一个\(x\),让你找到一种生成树,边权为\(w_1,w_2,...,w_{n-1}\),它的花费为\(\displaystyle\sum_{i=1}^{n-1}\mid w_i-x\mid\),答案为最小的花费。前p个询问给的数字为\(q_1,q_2,...,q_p\),对于第\(p+1\)\(k\)次询问,\(q_j=(q_{j-1}\cdot a+b)\ mod\ c\)。最后输出所有答案的异或和。\((2\leq n\leq 50,n-1\leq m\leq300,0\leq w_i\leq10^8,1\leq p\leq10^5,p\leq k\leq10^7)\)\((0\leq a,b\leq10^8,1\leq c\leq10^8,0\leq q_j<c)\)

思路

简单讲一下思路。很容易就能想到\(kruskal\),但是每次又要重新排序,应该会\(t\)。但是我们可以想一下更改\(x\)对排序的影响,也就是对于\(w_a\)\(w_b\),当\(x\)从小于\(\frac{w_a+w_b}{2}\)到大于\(\frac{w_a+w_b}{2}\)\(w_a\)\(w_b\)得交换一个位置。而所有排序的情况只有\(m^2\)种,所以很好预处理,然后对于每一个\(x\),便能得到对应的答案。

posted @ 2022-02-11 19:58  Jerry_Black  阅读(47)  评论(0)    收藏  举报