Codeforces Round 895 (Div. 3)

 比赛题目链接:https://codeforces.com/contest/1872

目录

A. Two Vessels

 B. The Corridor or There and Back Again

C. Non-coprime Split

 D. Plus Minus Permutation

 E. Data Structures Fan

 F. Selling a Menagerie

 G. Replace With Product


A. Two Vessels

题意大概就是有三个船,A船与B船有水,然后有一个空船C,空船C一次最多能从其他船转移c克水,问最少几次能用C船将A与B船的水量相等,每次转移的水量可以不是整数。

题意很简单,思路也很简单,假设从A船转移c克水到B船,那么A船的重量就会减少c,B船就会增加c,因此总共需要转移的水量就是两船的水量的差值的绝对值的一半,然后判断一下需要几次即可

代码如下:

#include<iostream>
#include<algorithm>
#include<cmath>

using namespace std;


int main()
{
    int t;
    double a,b,c;
    cin>>t;
    while(t--)
    {
        int ans=0;
        cin>>a>>b>>c;
        double mid=abs(a-b)/2;
        double step=mid/c;
        if(step>(int)step)
            ans=step+1;
        else 
            ans=(int)step;
        cout<<ans<<endl;
    }
    return 0;
}

 B. The Corridor or There and Back Again

题目大意就是在一条走廊上,有很多个房间,我们初始时站在第1个房间中,从左到右有无数个房间,然后向右走,每移动一个房间需要花费一秒的时间,然后最后我们要回到初始的房间,在其中的一些房间中,可能会存在陷阱,陷阱会有一个触发的时间,为第一次进入到这个房间的时间加上触发的时间,即如果第二个房间存在一个陷阱,触发的时间为2秒,那么这个陷阱将在1+2=3秒时起作用,我们要安全的回到初始的位置,问我们最远能走到多远的房间。

首先我们可以将所有的陷阱按房间号为第一关键字,陷阱的触发时间为第二关键字排序,然后每走到一个房间更新一下,能走到最远的房间是多少,注意后面更新的时候只能减不能加

代码如下:

#include<iostream>
#include<algorithm>

using namespace std;

typedef pair<int,int>pii;

const int N=110;

int n;
pii a[N];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)scanf("%d%d",&a[i].first,&a[i].second);
        sort(a,a+n);
        int maxs=0;
        maxs=a[0].first+(a[0].second-1)/2;//第一个陷阱房间能走到的最远距离
        for(int i=1;i<n;i++)
        {
            int s=a[i].first+(a[i].second-1)/2;//后面的陷阱房间能走到的最远距离
            if(s<maxs)maxs=s;//更新最远的距离,只能减不能加
        }
        printf("%d\n",maxs);
    }
    return 0;
}

C. Non-coprime Split

题目大意就是给定一个区间【l,r】,找到两个数a,b满足两个条件

1)l<=a+b<=r                2)gcd(a,b)!=1

思路其实很简单,首先我们要知道一个公式,就是更相减损术,即,如果a>b,gcd(a,b)=gcd(a-b,b),可以发现第二个式子中,两个因子加起来恰好就是a,那么我们考虑一下如果给定的l和r相等的情况,那么我们需要找到一个b使得gcd(l,b)!=1,那么l一定不能是质数,且b能整除l的话,就可以找到一个b满足上述条件,那么答案就是l-b和b。因此,扩展到其他情况,我们可以遍历l到r这个区间,只要这个区间中有一个数不是质数,且能找到一个它的因子,就得到了答案,因为两个质数一般不会连续出现,所以我们的速度其实是很快的。

最后要注意的是,我们遍历时,左区间最低为4,因为1,2,3都是质数,且如果是3,我们找到的第一个因子为3,答案会变成0,3,不符合题意。

代码如下:

#include<iostream>
#include<algorithm>

using namespace std;

typedef long long ll;

void slove(int l,int r)
{
    for(int i=max(l,4);i<=r;i++)//遍历给定区间
    {
        for(int j=2;j<=i/j;j++)//找到它的一个因子,即表示i不是质数,且符合答案
        {
            if(i%j==0)
            {
                printf("%d %d\n",j,i-j);
                return;
            }
        }
    }
    printf("-1\n");//都没找到答案即为-1
    return;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        slove(l,r);
    }
    return 0;
}

 D. Plus Minus Permutation

题目意思就是给三个参数,n,x,y,给一个1~n的序列a,找到一个排列,使得下标为x的倍数的a[x]的总和减去下标为y的倍数的a[y]的值最大

那么显而易见,我们需要让前面那部分尽可能大,后面那部分尽可能小,前面那部分的数的个数一共是n/x个,后面那部分的个数一共是n/y个,对于一些下标,它可能即是x的倍速,也是y的倍速,那么它可能会先加上后减去,所以前面那部分的个数和后面那部分的个数要减去这些重合的下标的个数,重合的下标的个数就是x和y的最小公倍数,对于前面那部分,我们就可以从n开始往前选择需要的个数,对于后面那部分,我们就从1开始往后选择需要的个数,这样得到的答案就是最大的

代码如下:

#include<iostream>
#include<algorithm>

using namespace std;

typedef long long ll;

int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll n,x,y;
        cin>>n>>x>>y;
        ll num1=n/x-n/((x*y)/gcd(x,y));//前面部分的个数
        ll num2=n/y-n/((x*y)/gcd(x,y));//后面部分的个数
        ll sum1=(ll)(2*n-num1+1)*num1/2;//前面部分的总和,用等差数列的求和公式求得
        ll sum2=(ll)(1+num2)*num2/2;//后面部分的总和
        ll ans=sum1-sum2;
        printf("%lld\n",ans);
    }
    return 0;
}

 E. Data Structures Fan

题目大意就是给定一个序列a1,a2,……an,然后给定一个长度为n得01序列,有两个操作

1)1 l r      将01序列中得【l,r】区间得0改成1,1改成0

2)2 g        对于01序列中所有是g的下标i,对应的所有的a[i]异或的值

这题很显然是一个线段树的题,但是这题有更简单的方法,运用异或的性质,我们可以用一个数x0存储所有si是0的ai的异或值,x1存储所有si是1的ai的异或值,如果要修改一个区间,对于x0来说,我们要消去这个区间所有si为0的ai的异或值,还要加上这个区间中所有si是1的ai的异或值,等价于我们再异或一遍这个区间的所有ai,因为两个相同的数异或后为0,对于之前没有的数,直接异或等价于加上这个数。对于x1同理,然后我们再预处理一个前缀异或和,就可以快速的得到一个区间的异或值

代码如下:
 

#include<iostream>
#include<algorithm>

using namespace std;

const int N=1e5+10;

int n,q;
int a[N],prefix_xor[N];
char str[N];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int x0=0,x1=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            prefix_xor[i]=prefix_xor[i-1]^a[i];//预处理前缀异或值
        }
        scanf("%s",str+1);
        for(int i=1;str[i];i++)
        {
            if(str[i]=='0')x0^=a[i];//所有si是0的ai的异或值
            else x1^=a[i];//所有si是1的ai的异或值
        }
        scanf("%d",&q);
        while(q--)
        {
            int op,l,r,g;
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d%d",&l,&r);
                x0=x0^prefix_xor[r]^prefix_xor[l-1];//x0异或上这个区间的异或值
                x1=x1^prefix_xor[r]^prefix_xor[l-1];//x1异或上这个区间的异或值
            }
            else
            {
                scanf("%d",&g);
                if(g==0)printf("%d ",x0);
                else printf("%d ",x1);
            }
        }
        printf("\n");
    }
}

 F. Selling a Menagerie

题目大意就是有一些小动物,我们要卖掉它们,要求得到的收入最大,对于不同的卖的顺序会得到不同的收入,对于每个i它害怕的动物是a【i】,卖出每个动物会得到c【i】的收入,如果i在a[i]前卖出,会得到2*c【i】的收入,否则会得到c【i】的收入

题解思路:因此对于卖出后得到的收入较大的动物,我们应该优先在其害怕的动物前卖出。首先,对于没有其他动物害怕的动物,我们可以最首先卖出,因为它的卖出不会影响到其他动物的收入,同时卖出了这个动物后,要考虑它害怕的动物还会不会影响到其他动物,如果不会,也可以直接卖出。然后对于剩下的所有动物,一定是一个循环,对于循环的最后一个,一定得到的单倍收入,因此我们要让卖出后得到的收入最低的动物放在循环的最后,然后往前依次是后一个动物能影响到的动物,因此就得到了我们的答案

代码如下:
 

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>

using namespace std;

const int N=1e5+10;

int n;
int a[N],c[N],in[N],v[N];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)v[i]=0,in[i]=0;//因为有多组测试数据,所以每次要初始化
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            in[a[i]]++;//记录a[i]能影响的动物的个数
        }
        for(int i=1;i<=n;i++)scanf("%d",&c[i]);
        queue<int>q;//用bfs求出所有影响其他动物的个数为0的动物
        for(int i=1;i<=n;i++)//首先将不能影响其他动物的动物入队
            if(!in[i])
                q.push(i);
        vector<int>ans;//记录答案
        while(!q.empty())
        {
            int t=q.front();q.pop();
            ans.push_back(t);
            v[t]=1,in[a[t]]--;//当一个动物i被卖掉以后,对应的a[i]能影响到的动物的数量要减1,被卖掉的动物要标记一下
            if(!in[a[t]])//如果为0,就入队
                q.push(a[t]);
        }
        for(int i=1;i<=n;i++)//剩下的动物都只能影响一个动物,因此是一个循环
        {
            vector<int>ans2;
            if(!v[i])//找到没有每卖掉的动物
            {
                for(int j=i;!v[j];j=a[j])ans2.push_back(j),v[j]=1;//记录下来当前动物所在的循环有的动物,同时标记被卖掉
                int minj=0,minc=c[ans2[0]];
                int len=ans2.size();
                for(int j=1;j<len;j++)//找到c[i]最小的动物,放在最后被卖掉
                {
                    if(c[ans2[j]]<minc)
                    {
                        minc=c[ans2[j]];
                        minj=j;
                    }
                }
                for(int j=1;j<=len;j++)//因为我们找到的是最后被卖掉的动物,因此反过来就是我们要卖的孙旭,这里是一个循环
                    ans.push_back(ans2[(minj+j)%len]);
            }
        }
        
        for(auto i:ans)printf("%d ",i);//最后输出答案
        printf("\n");
    }
    return 0;
}

 G. Replace With Product

题目大意就是给一个序列a1,a2,……an,我们可以用其中一个区间的乘积替换掉这个区间的几个数,找到替换哪个区间的数,使得我们最后得到的序列总和是最大的

题解思路:首先,如果序列的乘积足够大,那么我们一定是将整个区间都替换掉是最好的选择。我们可以假定这个足够大的数是10^9,那么题目就转换成了找到一个区间,如果这个区间的一部分或者是整个区间的乘积大于10^9,我们就替换掉这整个区间,同时要注意,区间的左右边界一定是大于1的数,因为如果左右边界是1的话,我们可以通过移动左右区间,使得序列总和加1.如果没有区间的乘积大于10^9,那么我们可以将所有大于1的数的下标用一个vector存起来,然后通过前缀和以及前缀乘的方式判断,替换掉哪个区间是最优的

代码如下:

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

typedef long long ll;

const int N=2e5+10;
const ll INF=1e9;

int n;
int a[N];

void solve()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int l=1,r=n;
    while(l<r&&a[l]==1)l++;//移动左边界使得边界大于1
    while(l<r&&a[r]==1)r--;//移动右边界使得边界大于1
    
    ll res=1;
    for(int i=l;i<=r;i++)//判断这个区间的部分区间或者整个区间是否大于一个极大的数
    {
        res*=a[i];
        if(res>=INF)
        {
            printf("%d %d\n",l,r);//如果有这样的区间的话,那么整个区间就是答案
            return;
        }
    }
    
    ll sum=0,ans;
    for(int i=1;i<=n;i++)sum+=a[i];//先求整个序列的总和,便于后面判断
    ans=sum;
    
    vector<int>v;//记录值大于1的下标
    for(int i=1;i<=n;i++)
        if(a[i]>1)
            v.push_back(i);
            
    int fl=1,fr=1;//初始时答案的左右边界都为1,如果最后没有合适的区间,等于不用替换
    for(int i=0;i<v.size();i++)//确定左边界
    {
        l=v[i];//左边界
        ll s=0;//从左边界开始的前缀和
        res=1;//从左边界开始的前缀乘
        for(int j=i;j<v.size();j++)//确定右边界
        {
            r=v[j];//右边界
            res*=a[r];//前缀乘
            s+=a[r]-1;//前缀和,这里减1便于后面求区间l到r这段区间的1的总和
            ll cur=sum-(r-l+1)-s+res;//(r-l+1)表示l到r这段区间中的1的总和,cur表示替换l到r这段区间后得到的值
            if(cur>ans)//如果替换l到r这段区间后置变大的了,就更新当前的最大值,同时记录下来当前修改的区间
            {
                ans=cur;
                fl=l,fr=r;
            }
        }
    }
    printf("%d %d\n",fl,fr);
    return;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        solve();
    }
    return 0;
}


 

Codeforces Round 894 (Div. 3) 是一个Codeforces举办的比赛,是第894轮的Div. 3级别比赛。它包含了一系列题目,其中包括题目E. Kolya and Movie Theatre。 根据题目描述,E. Kolya and Movie Theatre问题要求我们给定两个字符串,通过三种操作来让字符串a等于字符串b。这三种操作分别为:交换a中相同位置的字符、交换a中对称位置的字符、交换b中对称位置的字符。我们需要先进行一次预处理,替换a中的字符,然后进行上述三种操作,最终得到a等于b的结果。我们需要计算预处理操作的次数。 根据引用的讨论,当且仅当b[i]==b[n-i-1]时,如果a[i]!=a[n-i-1],需要进行一次操作;否则不需要操作。所以我们可以遍历字符串b的前半部分,判断对应位置的字符是否与后半部分对称,并统计需要进行操作的次数。 以上就是Codeforces Round 894 (Div. 3)的简要说明和题目E. Kolya and Movie Theatre的要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Codeforces Round #498 (Div. 3) (A+B+C+D+E+F)](https://blog.csdn.net/qq_46030630/article/details/108804114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Codeforces Round 894 (Div. 3)A~E题解](https://blog.csdn.net/gyeolhada/article/details/132491891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值