Educational Codeforces Round 21 题解

本文解析了CodeForces平台上的六道题目,包括简单的数学问题、前缀和应用、复杂条件下的分配问题、数组划分挑战、背包问题变种及卡片游戏策略等。每道题目都提供了详细的解题思路和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

比赛链接:http://codeforces.com/contest/808

A. Lucky Year 水题

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int  main()
{
    LL n;
    scanf("%lld", &n);
    if(n<10){
        printf("1\n");
        return 0;
    }
    LL  dd = 0;
    LL  t = n;
    while(t){
        dd++;
        t/=10;
    }
   // cout<<dd<<endl;
    LL  mod = 1;
    for(LL  i=1; i<=dd; i++) mod=mod*10;
    LL  mod2 = mod/10;
    LL  low = n/mod2;
    low = (low+1)*mod2;
    LL  ans = low-n;
    printf("%lld\n", ans);
    return 0;
}

B. Average Sleep Time

题意:就是n个数,求连续的k个数相加之和的平均值。
解法:模拟一下就可以了。用前缀和求。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,k;
LL a[200010], ans, presum[200010], cnt=0;
int main()
{
    scanf("%d%d", &n,&k);
    for(int i=1; i<=n; i++) scanf("%lld", &a[i]);
    for(int i=1; i<=n; i++) presum[i]=presum[i-1]+a[i];
    ans = 0;

    for(int i=1; i+k-1<=n; i++){
        ans = ans+(presum[i+k-1]-presum[i-1]);
        cnt++;
    }
    printf("%.8f\n", 1.0*ans/(1.0*cnt));
    return 0;
}

C. Tea Party
题意:
Polycarp招待朋友,给朋友们倒茶,朋友们的茶杯容量不一样,Polycarp的茶壶容量是w,w小于朋友茶杯满容量的总和。倒茶时需要满足几个条件。
1.每个人的茶杯必须至少倒一半。
2.每个人的茶杯倒整数的容量。
3.如果甲茶杯的容量大于乙茶杯的容量,那么甲茶杯里的茶不能少于乙。
4.Polycarp茶壶里的茶必须倒光。

解法:首先给每个杯子倒茶(a[i]+1)/2毫升,如果茶壶水不够,输出-1.
否则,把当前茶壶剩下水,倒给其他水杯中,从容量最大的开始,直接倒满,如果还有剩余,就给第二大的水杯倒满,依次类推,直到茶壶为空。

#include <bits/stdc++.h>
using namespace std;
struct node{
    int a,b,id;
    node(int a=0,int b=0,int id=0):a(a),b(b),id(id){}
    bool operator<(const node &rhs)const{
        return a>rhs.a;
    }
}a[110];

int main(){
    int n, w, sum=0;
    scanf("%d %d", &n,&w);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i].a);
        a[i].b=(a[i].a+1)/2;
        a[i].id=a[i].a;
        sum+=(a[i].a+1)/2;
        a[i].a-=a[i].b;
    }
    if(sum>w){
        puts("-1");
        return 0;
    }
    for(int i=1; i<=n; i++) w-=a[i].b;
    for(int i=1; i<=n; i++){
        if(w){
            int mx = 0, pos = 0;
            for(int j=1; j<=n; j++){
                if(a[j].a&&mx<a[j].id){
                    mx=a[j].id;
                    pos=j;
                }
            }
            if(pos==0) break;
            else{
                int temp = min(a[pos].a, w);
                a[pos].b+=min(a[pos].a,w);
                a[pos].a-=min(a[pos].a,w);
                w-=temp;
            }
        }
    }
    if(w){
        puts("-1");
    }
    else{
        for(int i=1; i<=n; i++) printf("%d ", a[i].b);
        printf("\n");
    }
    return 0;
}

D. Array Division
题意:给出一个数组,求能否移动一个数,使得某个位置前的所有数的和是总和的一半。
解法:输入预处理下,求出前缀和,因为所有的数都是整数且互不相同,所以前缀和肯定是递增的。
对于任意一个数,如果将它放在某个数的后面使得条件满足,那么就有 sum[x] = sum/2 - b[i]; ( x>i)
如果将它放在某个数的前面使得条件满足,那么同上就有 sum[x] = sum/2 + b[i]; (x < i);

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, a[100010];
LL sum[100010];
LL sum2[100010];
int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%lld", &a[i]);
    for(int i=1; i<=n; i++) sum[i]=sum[i-1]+a[i], sum2[i]=sum[i];
    if(sum[n]&1){
        puts("NO");
        return 0;
    }
    sum[n]/=2;
    int flag = 0;
    for(int i=1; i<=n; i++){
        int l = lower_bound(sum2+1,sum2+i-1,sum[n]-1LL*a[i])-sum2;
        if(sum2[l]+a[i]==sum[n]&l!=i) flag = 1;
        int r = lower_bound(sum2+i+1,sum2+n,sum[n]+1LL*a[i])-sum2;
        if(sum2[r]-a[i]==sum[n]) flag=1;
    }
    if(flag) puts("YES");
    else puts("NO");
}

E. Selling Souvenirs
题意: 分别有1,2,3三种重量的物品 价值为ci 问重量为w的情况下 价值最大化其实就是增大版本的01背包
解法: 依次计算出1 2 3 背包的最大值,只有w=1 的时候 直接排序 dp[i]=dp[i-1]+c;当有重量为2的时候 比较dp[i] 之中最小的两个1相加 是否小于w=2的情况 小于直接替换;当有重量为3的时候 直接 遍历3的使用次数就可以了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, m;
priority_queue<int>q[4];
LL dp[4][300010];

int main()
{
    scanf("%d %d", &n,&m);
    for(int i=1; i<=n; i++){
        int w, c;
        scanf("%d %d", &w,&c);
        if(w==1) q[1].push(c);
        else if(w==2) q[2].push(c);
        else q[3].push(c);
    }
    int sz1 = q[1].size(), sz2 = q[2].size();
    for(int i=1; i<=min(sz1, m); i++){
        dp[1][i]=dp[1][i-1]+q[1].top();
        q[1].pop();
    }
    for(int i=sz1+1; i<=m; i++) dp[1][i]=dp[1][i-1];
    LL c, l=1, sum=0, t=0;
    for(int i=1; i<=min(sz2, m); i++){
        c = q[2].top(); q[2].pop();
        for(int j=l; j<=m; j++){
            l=j;
            if(j>=2*(t+1)&&dp[1][j-t*2]<dp[1][j-t*2-2]+c){
                dp[2][j]=dp[1][j-t*2-2]+sum+c;
                sum+=c;
                t++;
                break;
            }
            else{
                dp[2][j]=dp[1][j-t*2]+sum;
            }
        }
    }
    for(int j=l; j<=m; j++) dp[2][j]=dp[1][j-t*2]+sum;
    sum = t = 0;
    dp[3][m] = dp[2][m];
    while(q[3].size()){
        t+=3;
        sum+=q[3].top();
        q[3].pop();
        if(t<=m){
            dp[3][m]=max(dp[3][m], dp[2][m-t]+sum);
        }
    }
    printf("%lld\n", dp[3][m]);
    return 0;
}

F. Card Game

题意:有n个卡片,每个卡片有三个值:p,c,l;现在让你找一个最小的L,使得满足选出来的卡片l<=L,并且所有卡片的p的和不小于k。选择卡片时有限制,任意两张卡片的c之和不能为质数。
解法:复制官方题解。

The most tricky part of the problem is how to check if some set of cards allows us to build a deck with the required power (not taking the levels of cards into account).

Suppose we have not more than one card with magic number 1 (if there are multiple cards with this magic number, then we obviously can use only one of these). Then two cards may conflict only if one of them has an odd magic number, and another has an even magic number — otherwise their sum is even and not less than 4, so it’s not a prime number.

This allows us to solve this problem as follows:

Construct a bipartite graph: each vertex represents a card, and two vertices are connected by an edge if the corresponding pair of cards can’t be put in a deck. Then we have to find the maximum weight of independent set in this graph. This can be solved using maximum flow algorithm: construct a network where source is connected with every “odd” vertex (a vertex that represents a card with an odd magic number) by an edge with capacity equal to the power of this card; then connect every “odd” vertex to all “even” vertices that are conflicting with this vertex by edges with infinite capacities; and then connect every “even” vertex to the sink by an edge with capacity equal to the power of the card (all edges have to be directed). Then the maximum power of the deck is equal to sum - mincut, where sum is the sum of all powers and mincut is the minimum cut value between the source and the sink (which is equal to the maximum flow).

This allows us to check if we can build a deck of required power using only some set of cards (for example, only cards with level less than or equal to some x).

大概就是按照每一个c的奇偶构成了二分图,要特殊处理c=1的情况,接下来就是把所有可能的和成为素数的连边,容量为INF,然后就转化成了最大权独立集,最大权独立集=总权-最小权覆盖集,对于最小权覆盖集我们用最小割来解。

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 200;
const int maxm = 30000;
const int MAXN = 2e5+5;
bool isprime[MAXN];
struct G
{
    int v, cap, next;
    G() {}
    G(int v, int cap, int next) : v(v), cap(cap), next(next) {}
} E[maxm];
int p[maxn], T, n, k;
int d[maxn], temp_p[maxn], qw[maxn]; //d顶点到源点的距离标号,temp_p当前狐优化,qw队列
void init()
{
    memset(p, -1, sizeof(p));
    T = 0;
}
void add(int u, int v, int cap)
{
    E[T] = G(v, cap, p[u]);
    p[u] = T++;
    E[T] = G(u, 0, p[v]);
    p[v] = T++;
}
bool bfs(int st, int en, int n)
{
    int i, u, v, head, tail;
    for(i = 0; i <= n; i++) d[i] = -1;
    head = tail = 0;
    d[st] = 0;
    qw[tail] = st;
    while(head <= tail)
    {
        u = qw[head++];
        for(i = p[u]; i + 1; i = E[i].next)
        {
            v = E[i].v;
            if(d[v] == -1 && E[i].cap > 0)
            {
                d[v] = d[u] + 1;
                qw[++tail] = v;
            }
        }
    }
    return (d[en] != -1);
}
int dfs(int u, int en, int f)
{
    if(u == en || f == 0) return f;
    int flow = 0, temp;
    for(; temp_p[u] + 1; temp_p[u] = E[temp_p[u]].next)
    {
        G& e = E[temp_p[u]];
        if(d[u] + 1 == d[e.v])
        {
            temp = dfs(e.v, en, min(f, e.cap));
            if(temp > 0)
            {
                e.cap -= temp;
                E[temp_p[u] ^ 1].cap += temp;
                flow += temp;
                f -= temp;
                if(f == 0)  break;
            }
        }
    }
    return flow;
}
int dinic(int st, int en, int n)
{
    int i, ans = 0;
    while(bfs(st, en, n))
    {
        for(i = 0; i <= n; i++) temp_p[i] = p[i];
        ans += dfs(st, en, inf);
    }
    return ans;
}
struct node{
    int p,c,l;
    void read(){
        scanf("%d %d %d", &p,&c,&l);
    }
}a[MAXN];
void work()
{
    for(int i=1; i<=n; i++) a[i].read();
    int s = 0, t = n+1;
    for(int i=1; i<=n; i++){//n100可以直接枚举
        int tot = 0, one = 0, p = 0;
        for(int j=1; j<=n; j++){
            if(a[j].l <= i){
                if(a[j].c == 1){
                    if(a[j].p > p){
                        one = j, p = a[j].p;
                    }
                }
                else{
                    tot += a[j].p;
                }
            }
        }
        tot += p;
        init();
        for(int j=1; j<=n; j++){
            if(a[j].l<=i&&(a[j].c>1||j==one)){
                if(a[j].c&1) add(s,j,a[j].p);
                else add(j,t,a[j].p);
            }
        }
        for(int j=1; j<=n; j++){
            if(a[j].l<=i&&(a[j].c%2||j==one)){
                for(int k=1; k<=n; k++){
                    if(a[k].l<=i&&a[k].c%2==0){
                        if(isprime[a[j].c+a[k].c]){
                            add(j, k, inf);
                        }
                    }
                }
            }
        }
        int flow = dinic(s, t, n+2);
        if(tot - flow >= k){
            printf("%d\n", i);
            return ;
        }
    }
    puts("-1");
}
int main()
{
    for(int i=2; i<=200000; i++) isprime[i]=true;
    for(int i=2; i<=200000; i++){
        if(isprime[i]){
            for(int j=i+i; j<=200000; j+=i){
                isprime[j]=false;
            }
        }
    }
    while(~scanf("%d %d", &n, &k))
    {
        work();
    }
    return 0;
}

G. Anthem of Berland

题意:给了一串,带有?,然后给了一个文本串,?可以替换成任何字符,问最后能得到的串和文本穿的最大匹配数。

解法://对t进行 KMP用类似 AC 自动机的方法预处理出t串的前i个字符加上字符j后其后缀最多能匹配t串多长的前缀。//设 dp(i,j)表示s串的前i个字符其后缀最长匹配到t串的前缀长度为j,t串的最多匹配次数

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
char s[N],t[N];
int ch[N][26], fail[N];
//对t进行 KMP用类似 AC 自动机的方法预处理出t串的前i个字符加上字符j后其后缀最多能匹配t串多长的前缀。
int main()
{
    scanf("%s", s+1);
    scanf("%s", t+1);
    int n=strlen(s+1), m = strlen(t+1);
    for(int i=1; i<=m; i++) ch[i-1][t[i]-'a']=i;
    for(int i=1, k=0; i<=m; i++){
        if(i>1){
            while(k&&t[k+1]!=t[i]) k=fail[k];
            if(t[k+1]==t[i]) fail[i]=++k;
            else fail[i]=0;
        }
        for(int j=0;j<26; j++){
            if(!ch[i][j]){
                ch[i][j]=ch[fail[i]][j];
            }
        }
    }
//    for(int i=1; i<=m; i++) printf("%d ",fail[i]);
//    for(int i=0; i<=m; i++){
//        for(int j=0; j<26; j++){
//            printf("%d ", ch[i][j]);
//        }
//        printf("\n");
//    }
    //设 dp(i,j)表示s串的前i个字符其后缀最长匹配到t串的前缀长度为j,t串的最多匹配次数
    vector <vector <int> > dp(n+1, vector<int>(m+1, -1));
    dp[0][0]=0;
    for(int i=0; i<n; i++){
        for(int j=0; j<=m; j++){
            if(dp[i][j] == -1) continue;
            for(int c=s[i+1]=='?'?0:(s[i+1]-'a'); c<(s[i+1]=='?'?26:(s[i+1]-'a'+1)); c++){
                dp[i+1][ch[j][c]] = max(dp[i+1][ch[j][c]], dp[i][j]+(ch[j][c]==m));
            }
        }
    }
    int ans=0;
    for(int i=0; i<=m; i++){
        ans = max(ans, dp[n][i]);
    }
    printf("%d\n", ans);
    return 0;
}
### Codeforces Educational Round 26 比赛详情 Codeforces是一个面向全球程序员的比赛平台,其中Educational Rounds旨在帮助参与者提高算法技能并学习新技巧。对于具体的Educational Round 26而言,这类比赛通常具有如下特点: - **时间限制**:每道题目的解答需在规定时间内完成,一般为1秒。 - **内存限制**:程序运行所占用的最大内存量被限定,通常是256兆字节。 - 输入输出方式标准化,即通过标准输入读取数据并通过标准输出打印结果。 然而,关于Educational Round 26的具体题目细节并未直接提及于提供的参考资料中。为了提供更精确的信息,下面基于以往的教育轮次给出一些常见的题目类型及其解决方案思路[^1]。 ### 题目示例与解析 虽然无法确切描述Educational Round 26中的具体问题,但可以根据过往的经验推测可能涉及的问题类别以及解决这些问题的一般方法论。 #### 类型一:贪心策略的应用 考虑一个问题场景,在该场景下需要照亮一系列连续排列的对象。假设存在若干光源能够覆盖一定范围内的对象,则可以通过遍历整个序列,并利用贪心的思想决定何时放置新的光源以确保所有目标都被有效照射到。这种情况下,重要的是保持追踪当前最远可到达位置,并据此做出决策。 ```cpp #include <bits/stdc++.h> using namespace std; bool solve(vector<int>& a) { int maxReach = 0; for (size_t i = 0; i < a.size(); ++i) { if (maxReach < i && !a[i]) return false; if (a[i]) maxReach = max(maxReach, static_cast<int>(i) + a[i]); } return true; } ``` #### 类型二:栈结构处理匹配关系 另一个常见问题是涉及到成对出现元素之间的关联性判断,比如括号表达式的合法性验证。这里可以采用`<int>`类型的栈来记录左括号的位置索引;每当遇到右括号时就弹出最近一次压入栈底的那个数值作为配对依据,进而计算两者间的跨度长度累加至总数之中[^2]。 ```cpp #include <stack> long long calculateParens(const string& s) { stack<long long> positions; long long num = 0; for(long long i = 0 ; i<s.length() ;++i){ char c=s[i]; if(c=='('){ positions.push(i); }else{ if(!positions.empty()){ auto pos=positions.top(); positions.pop(); num+=i-pos; } } } return num; } ``` #### 类型三:特定模式下的枚举法 针对某些特殊条件约束下的计数类问题,如寻找符合条件的三位整数的数量。此时可通过列举所有可能性的方式逐一检验是否符合给定规则,从而统计满足要求的结果数目。例如求解形如\(abc\)形式且不含重复数字的正整数集合大小[^3]。 ```cpp vector<int> generateSpecialNumbers(int n) { vector<int> result; for (int i = 1; i <= min(n / 100, 9); ++i) for (int j = 0; j <= min((n - 100 * i) / 10, 9); ++j) for (int k = 0; k <= min(n % 10, 9); ++k) if ((100*i + 10*j + k)<=n&&!(i==0||j==0)) result.emplace_back(100*i+10*j+k); sort(begin(result), end(result)); return result; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值