ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Educational Codeforces Round 104 (Rated for Div. 2)A-E题解

2021-02-16 20:00:54  阅读:164  来源: 互联网

标签:Educational Rated const int 题解 cin st 平局 define


Educational Codeforces Round 104 (Rated for Div. 2)A-E题解

比赛链接:https://codeforces.ml/contest/1487

A题
简单结论,暴力

A的题意一开始没讲清楚,光看了前两组样例也没发现不对,等敲完代码了才看见第三组样例。出了个公告澄清题意后直接秒掉。花了10分钟血亏。

题意:
给定n个英雄(n最大为100),每个英雄一开始都有他自己的等级(1到100)。现在你要安排这些英雄按照一定顺序进行一对一的对决,每一个人可以被任意选择进行对决无数次。
对决双方如果等级相同,那么对决平局,双方等级无影响。
对决双方如果等级不同,那么等级高的人赢,他的等级+1,输的人等级不变。
先获得100500次胜利的英雄为最后的赢家。

问有多少个英雄可能成为最后赢家。

思路:
注意到一开始等级最低的那些英雄,不管和谁对决都没法赢,也就无法通过胜利增加自己的等级,也就是永远不可能对决获胜,因此这些英雄是不可能成为最后的赢家的。
而除了这些等级最低的英雄外,其他英雄都可以先欺负等级最低的英雄,提升自己的等级,并取得胜利。

因此除了一开始等级最低的英雄之外,其他所有英雄都可能是最后的赢家。
数据比较小,直接暴力就是了,不要再多过脑子,比赛时候过题速度是第一的。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+7;
const double eps=1e-6;
const int mod=1e9+7;

int main()
{
    IOS
    int t;cin>>t;
    while(t--)
    {
        int n;cin>>n;
        vector<int>num(n);
        for(int i=0;i<n;i++) cin>>num[i];
        sort(num.begin(),num.end());
        int ans=n;
        for(int i=0;i<n;i++) if(num[i]==num[0]) ans--;
        cout<<ans<<endl;
    }
}

B题
数学

题意:
在一个有n个格子的环上,有两只猫,第一秒种的时候,A在格子1上,B在格子n上。
随着时间一秒一秒经过,A会按照1,2,3,…,n,1,2,…,n,1,2…这样的顺序不断循环移动。
而B会按照n,n-1,n-2,…2,1,n,n-1,n-2…这样的顺序不断循环移动。
如果某个时刻,A和B移动到了同一个格子的话,A会按照上述规则继续移动一个位置,避免和B在同一个地方。

给定n和时间第k秒,询问第k秒的时候A猫在哪个格子。

思路:
首先n是偶数的情况比较好考虑,
n是偶数的时候,两只猫刚好交错而过,交错而过之后,由于这是个环,我们换个位置来看,就又和最开始的情况一样了----两只猫之间隔着n-2个格子。
也就是说n是偶数的时候,B猫不会对A猫的移动产生影响,所以直接计算k%n就是了。

而n是奇数的时候,在经过[n/2]秒之后,A和B猫会在中间相遇,此时A猫要多走一格避开B猫。而此时同样换个位置来看,又和初始状态一样了,两只猫中间隔着n-2个格子。
也就是说每[n/2]秒,A猫会多走一格。
因此计算(k+(k-1)/(n/2))%n即可。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+7;
const double eps=1e-6;
const int mod=1e9+7;

int main()
{
    IOS
    int t;cin>>t;
    while(t--)
    {
        int n,k;cin>>n>>k;
        int ans=0;
        if(n&1) ans=(k+(k-1)/(n/2))%n;
        else ans=k%n;
        if(ans==0) ans=n;//注意最后位置是n的时候取余运算会变成0,要变回来
        cout<<ans<<endl;
    }
}

C题
构造

题意:
有n支球队,两两之间都要进行一次比赛,也就是总共有n(n-1)/2场比赛。
如果比赛平局,两支球队积分各自+1。
如果比赛不平局,获胜球队积分+3,失败球队积分不变。

现在你需要构造所有比赛的胜负情况,使得所有球队的分数在最后是相同的,并且平局的场次尽可能少。

思路:
先考虑n为奇数的情况,比较容易。
对于每支球队来说,都要与其他n-1支队伍比赛,有n-1场,偶数场的比赛。那么我们如果找到一种方案使得每支球队都有(n-1)/2场胜场和(n-1)/2场负场的分配方案,由于这种方案不存在平局,如果存在的话就必然是平局场次最少的。
我们可以按照下述规则来构造:
队伍i和队伍j进行比赛(i<j),如果i和j奇偶性相同,则让队伍i获胜,如果奇偶性不同则让队伍j获胜。

这种方案的话,对于队伍i来说,
和编号大于i的队伍比,共有[(n-i)/2]的胜场,
和编号小于i的队伍比,共有[i/2]的胜场,
由于n为奇数,易得[(n-i)/2]+[i/2]=(n-1)/2。满足上述要求。

考虑完n为奇数的构造方案,再来考虑n为偶数的构造方案。
n为偶数的时候,对于每支球队来说,都要与其他n-1支队伍比赛,有n-1场,奇数场的比赛。
那么我们再尽可能平均分配胜负场,最后多出来一场分配平局。
此处可以利用反证法证明,每支队伍的比赛胜负情况不存在平局的情况是不存在的,因为每支队伍最后分数相同,不存在平局的话,那么每支队伍的胜场数必然相同,而由于每支队伍要参加的比赛总场数为奇数,那么所有队伍胜场和负场的总和就不统一了。

我们可以让1和2,3和4,5和6,…,n-1和n之间打一场平局,先分配完平局。接下来的胜负场再按照n是奇数的情况去构造就可以了。
实际上就是先分配掉平局,使得情况变为类似n是奇数的情况。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+7;
const double eps=1e-6;
const int mod=1e9+7;

int main()
{
    IOS
    int t;cin>>t;
    while(t--)
    {
        int n;cin>>n;
        if(n%2==1)//奇数球队的时候直接平均分配胜负场即可
        {
            for(int i=1;i<=n;i++)
                for(int j=i+1;j<=n;j++)
                    if((i+j)%2==1) cout<<1<<' ';
                    else cout<<-1<<' ';
        }
        else//偶数的时候每支球队安排一场平局,其余同上构造
        {
            for(int i=1;i<=n;i++)
                for(int j=i+1;j<=n;j++)
                    if(j==i+1&&i%2==1) cout<<0<<' ';
                    else if((i+j)%2==1) cout<<1<<' ';
                    else cout<<-1<<' ';
        }
        cout<<endl;
    }
}

D题
数学,结论

题意:
给定一个整数n,询问用三条小于等于n的整数值作为三角形的边,我们规定三条边为a,b,c且a<=b<=c。
在满足c=a2-b的情况下,直角三角形的组合有多少种。

思路:
直角三角形勾股定理c2=a2+b2,与c=a2-b联立,得到c2-b2=b+c=a2
满足这个条件的b和c有什么性质呢,那就是c=b+1。(此处可以通过设c=b+x求出x=1,不再赘述)
而b+c有要是一个完全平方数。因此实际上就是在问,对于x在[2,n]这个区间内,x+x-1为完全平方数的有几对。

直接计算sqrt(n+n-1),即为a的最大值,容易注意到此处的a不能取1,因此去掉1的情况。
总方案数就是(sqrt(n+n-1)-1)/2种。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+7;
const double eps=1e-6;
const int mod=1e9+7;

int main()
{
    IOS
    int t;cin>>t;
    while(t--)
    {
        int n;cin>>n;
        int temp=sqrt(n+n-1);
        cout<<(temp-1)/2<<endl;
    }

E题
dp思想,数据结构

挺没营养的一道题目——指没什么思维量,就直接实现。

题意:
有四类菜色要选择,
四类菜色分别有n1,n2,n3,n4种。(最大值均为15e4),每类菜色都要选择其中一种。
其中第一类和第二类有m1种组合是不允许的,第二类和第三类中有m2种组合是不允许的,第三类和第四类中又有m3种组合是不允许的。(m1,m2,m3最大值均为2e5)
每种菜色都有价格,现在询问每类菜色选择一种,最低的价格是多少。如果不存在可选择的方案,输出-1。

思路:
用dp[i][j]代表从第一轮开始选,选到i+1轮使用第j种的最少花费,很容易想到这样的思路dp转移。
但是注意到组合的情况很多,还有一些组合是不被允许的。暴力转移的话复杂度是必然tle的。

这里可以使用线段树维护区间最小值,单点修改,查询区间当前最小值即可,降低一个n为log就可以了。
或者也可以用map记录所有不被允许的组合,对上一轮的费用结果从小到大排个序,每次暴力去找最小的允许和当前匹配的方案即可。由于不被允许带 方案最多2e5种,因此最多暴力循环2e5次去找,并不会超时。

直接for三遍上述操作即可,挺没营养的一道题。
下面给出线段树单点修改的解法。

#include<bits/stdc++.h>
#define ll long long
#define INF 2000000000//定义一个足够大的值作为无穷大
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=15e4+7;
const double eps=1e-6;
const int mod=1e9+7;

int ans=INF;
int n[4];
int need[4][maxn];//保存每种菜要多少花费
int dp[4][maxn];//dp[i][j]代表从第一轮开始选,选到i+1轮使用第j种的最少花费

struct Node
{
    int l,r;
    int num;//保存区间最小值
};
Node st[maxn<<2];

void build(int l,int r,int loca,int cas)
{
    st[loca].l=l;st[loca].r=r;
    if(l==r) {st[loca].num=dp[cas][l];return;}
    int mid=(l+r)>>1;
    build(l,mid,loca<<1,cas);
    build(mid+1,r,loca<<1|1,cas);
    st[loca].num=min(st[loca<<1].num,st[loca<<1|1].num);
}

void change(int tar,int loca,int x)//单点修改位置tar的值为x
{
    if(st[loca].l==st[loca].r)
    {
        st[loca].num=x;
        return;
    }
    int mid=(st[loca].l+st[loca].r)>>1;
    if(tar<=mid) change(tar,loca<<1,x);
    else change(tar,loca<<1|1,x);
    st[loca].num=min(st[loca<<1].num,st[loca<<1|1].num);
}

bool cmp(pair<int,int>a,pair<int,int>b)
{
    return a.second<b.second;
}

vector<pair<int,int>>buyao;//保存哪些组合是不允许的
vector<pair<int,int>>daichuli;//保存上一种情况哪些被删除掉了,当前要把这些值加回来

int main()
{
    IOS
    for(int i=0;i<4;i++) cin>>n[i];
    for(int i=0;i<4;i++)
        for(int j=1;j<=n[i];j++)
            cin>>need[i][j];
    for(int j=1;j<=n[0];j++) dp[0][j]=need[0][j];
    for(int i=1;i<4;i++)
    {
        build(1,n[i-1],1,i-1);//根据上次的dp结果重新建树
        int m;cin>>m;
        buyao.clear();
        buyao.resize(m);
        for(int j=0;j<m;j++) cin>>buyao[j].first>>buyao[j].second;
        sort(buyao.begin(),buyao.end(),cmp);//按照当前选择的种类的序号从小到大排序

        daichuli.clear();
        int now=0;
        for(int j=1;j<=n[i];j++)
        {
            for(int k=0;k<daichuli.size();k++)
                change(daichuli[k].first,1,dp[i-1][daichuli[k].first]);//把上轮被删掉了的加回来
            daichuli.clear();
            while(now<m&&buyao[now].second==j)//和当前的第j种无法组合的全部删掉,并且用daichuli记录,在下次循环中加回来
            {
                change(buyao[now].first,1,INF);
                daichuli.push_back(buyao[now]);
                now++;
            }
            dp[i][j]=st[1].num;//直接取当前剩余总区间的最小值
            if(dp[i][j]!=INF) dp[i][j]+=need[i][j];//如果等于INF无穷大的话,就代表不存在能与j组合的情况,不再做处理
        }
    }

    for(int j=1;j<=n[3];j++)
        if(dp[3][j]<ans) ans=dp[3][j];
    if(ans==INF) cout<<-1<<endl;
    else cout<<ans<<endl;
}

标签:Educational,Rated,const,int,题解,cin,st,平局,define
来源: https://blog.csdn.net/StandNotAlone/article/details/113827118

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有