【解题报告】CF DIV2 #ROUND 709 A~C

【解题报告】CF DIV2 #ROUND 709 A~D

比赛链接
当开到这场的时候我意识到,这场游戏的性质已经变了。无奖竞猜我会连着掉分多少次

A. Prison Break

思路
一眼水题,然而开局网络爆炸,等了好久才交成
代码

#include<bits/stdc++.h>
using namespace std;
 
int T;
int main()
{
    cin>>T;
    while(T--)
    {
        int a,b;
        cin>>a>>b;
        cout<<a*b<<endl;
    }
 
    return 0;
}

B. Restore Modulo

思路
数论+分类讨论+模拟
不知道为啥,貌似最近老被取模题整。
参考了这位大佬的题解

读题读题读题!!!数据范围数据范围数据范围!!!
在这里插入图片描述
看到最后一条输出的提示了吗,c是<m的。这意味着在最后一类情况中
在这里插入图片描述
a i = ( a i − 1 + c ) m o d    m a_i=(a_{i-1}+c)\mod m ai=(ai1+c)modm可以转化为如两种情况
A:如果 a i − 1 + c > = m a_{i-1}+c>=m ai1+c>=m那么 a i = a i − 1 + ( c − m ) a_i=a_{i-1}+(c-m) ai=ai1+(cm),且由于 c − m < = 0 c-m<=0 cm<=0,所以 a i < = a i − 1 a_i<=a_{i-1} ai<=ai1
B:如果 a i − 1 + c < m a_{i-1}+c<m ai1+c<m那么 a i = a i − 1 + c a_i=a_{i-1}+c ai=ai1+c,且 a i > a i − 1 a_i>a_{i-1} ai>ai1
对于给出的序列我们可以分成以下这些情况
①一直满足B,即 a i = a i − 1 + c a_i=a_{i-1}+c ai=ai1+c一直成立,恒定增加,差值为定值,那么m可以无限大输出0

1 2 3 4 5
a1+c<m
a2+c<m
a3+c<m
a4+c<m
m>max(a1,a2,a3,a4)+c即m>a1+c

②一直满足A,即 a i = a i − 1 + ( c − m ) a_i=a_{i-1}+(c-m) ai=ai1+(cm)一直成立,恒定减少,差值为定值,那么m最大为 m = a n − 1 + c − 1 m=a_{n-1}+c-1 m=an1+c1所以也输出0,如果差值减少的差值并不相等那么输出-1(之前没考虑的)

5 4 3 2 1
a1+c>=m
a2+c>=m
a3+c>=m
a4+c>=m
m<=min(a1,a2,a3,a4)+c即m<a4+c

然而这并不正确,如果所有数都相等的话,
③既有满足A的,也有满足B的

我们可以通过恒定递增的部分来确定c的值,如果c是不唯一的,那么直接输出-1。
例如下面的

C不唯一的情况

1 3 5 7 4 1 10 19
a1+c<m
a2+c<m
a3+c<m
a4+c>=m
a5+c>=m
a6+c<m
a7+c<m

如果C唯一,那么我们可以通过下降段来求出m,如果m不唯一那么输出-1,如果唯一输出m
(原本这里以为是要用不等关系的约束条件来搞,结果暴毙)

在这里插入图片描述
求m

1 9 17 6 14 3
c=8
17+8-m=6
m=(17-6)+8
   -差    c

然而如果按照上面描述写代码的话会发现还是会WA,还需要补充以下两点。如果n为1的话输出0。如果得出的m>=max(a[i])输出-1。补充上这两点后就可以AC啦!

代码

#include<bits/stdc++.h>
using namespace std;
int T;
const int N=1e5+10;
int a[N],d[N];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        int maxa=-1;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            maxa=max(maxa,a[i]);
        }
        if(n==1)puts("0");
        else
        {
            set<int>sa,sb;
            sa.clear(),sb.clear();
            for(int i=1;i<n;i++)
            {
                d[i]=a[i]-a[i-1];
                if(d[i]>=0)sa.insert(d[i]);
                else sb.insert(d[i]);
            }
            int siza=sa.size(),sizb=sb.size();
            if(siza==1&&sizb==0)puts("0");
            else if(siza==0&&sizb==1)puts("0");
            else if(siza>1||sizb>1)puts("-1");
            else if(siza==1&&sizb==1)
            {
                int c=*sa.begin();
                int m=c-*sb.begin();
                if(m<=c||m<=maxa)puts("-1");
                else printf("%d %d\n",m,c);
            }
        }
    }
    return 0;
}

C. Basic Diplomacy

题意
有n个朋友,总共m天,每天选择一个和一个朋友玩,每个朋友总选择次数不能超过 ⌈ m 2 ⌉ \lceil\frac m 2\rceil 2m次,现在给出m天每天可以选择朋友。如果有可行方案输出YES和满足条件的选择方式。如果不满足就输出NO
思路
贪心题。
比赛的时候稍微看了几眼,首先想到的是只有一个的选择的要先选,那么衍生出一个贪心就是先可选人数从小到大排序,然后每天选剩余可选次数最多的那个人。
比赛的时候正解思路差不多就是这样了,然后死在了代码实现。

原本一直在想用堆求最值,实际上总的 k i k_i ki是比较小的,所以直接暴力+映射就完事啦!

赛后一发秒了,离谱!
代码实现有些技巧,因为N是2e5级别的所以直接开二维数组是会爆空间的,所以利用vector数组来实现。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef pair<int,int> PII;
int T;
vector<PII>v[N];
int t[N];//统计用了多少
int ans[N];

bool cmp(vector<PII>a,vector<PII>b)
{
    if(a.size()<b.size())return 1;
    return 0;
}
bool cmp_2(PII a,PII b)
{
    int x=a.second,y=b.second;
    if(t[x]<t[y])return 1;
    return 0;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        memset(t,0,sizeof t);
        memset(ans,0,sizeof ans);
        memset(v,0,sizeof v);
        for(int i=1;i<=m;i++)
        {
            int k;
            scanf("%d",&k);
            for(int j=0,x;j<k;j++)
            {
                scanf("%d",&x);
                v[i].push_back(make_pair(i,x));//v[i]表示第i天
            }
        }
        sort(v+1,v+1+m,cmp);
        
        bool flag=1;;
        for(int i=1;i<=m;i++)
        {
            sort(v[i].begin(),v[i].end(),cmp_2);
            PII tmp=*v[i].begin();//tmp为使用次数最小的
            int day=t[tmp.second];//用了多少天
            if(day+1>(m+1)/2)
            {
                puts("NO");
                flag=0;
                break;
            }
            else
            {
                ans[tmp.first]=tmp.second;//第几天=选第几个人
                t[tmp.second]++;//选择的这个人使用次数+1
            }

        }
        if(flag)
        {
            puts("YES");
            for(int i=1;i<=m;i++)printf("%d ",ans[i]);
            printf("\n");
        }


    }
    return 0;
}

反思

A:

一眼题,唯一的希望就是校园网能快一点(卑微)

B:

注意数据范围
当c<mod的时候我们可以吧取模操作直接转化为±操作。
分类讨论的思想:A,B两种情况,我们可以讨论只有A,只有B,AB混合
注意观察数据范围尤其是0,1这种边界数据
注意检测结果的合法性

C:

看清楚总的数据量,不一定要最优化,有时候暴力整一遍是可以过的
求最值:暴力扫一遍,排序取头,利用堆(cf一般用不上啥数据结构)而且堆的特点是动态调整,你没事用堆干嘛,排序不香吗
可用次数最多的转化为用的最少的,然后直接记录使用次数这样比记录剩余可用次数要方便很多
如果开数组爆空间的话可以考虑开vector
贪心思想:先安排限制严格的,比如非选这个不可。或者考虑先剩余可利用次数多的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值