Codeforces Round #816 (Div. 2) 题解 A,B,C

Dashboard - Codeforces Round #816 (Div. 2) - Codeforces

A. Crossmarket

题目大意

给你一个 n*m 的矩阵,每移动一个格子都要消耗一个单位的能量,两个点分别在矩阵的左上角(1,1)和左下角(n,1),其中当一个人经过另一个人走过的格子时,可以传送到另一个人走过的任意格子(消耗一个单位的能量),求他们分别到达对角位置所需要的能量最小是多少。

解题思路

这题最特殊的点就在于传送这个技能,我们可以把它用到最长的边上,也就是说,对于任意一个矩阵我们需要走两遍宽度,但只需要走一次长度,另外再加上传送所需要的 1 就可以了

记得特判一下 1*1 的情况

代码示例

#include
#include
#include
#include
#include
using namespace std;
 
int t;
int n,m;
 
int main(){
    cin>>t;
    while(t--){
        cin>>n>>m;
        if(n>m) swap(n,m); 
        if(n==1&&m==1) cout<<"0"<

B. Beautiful Array

题目大意

给你 n 个空,被除数 k,n个向下取整的 (ai/k) 的求和等于 b,并且 ai 的和等于 s。请你把这个数组 a 给输出出来。如果不存在就输出 -1。

解题思路

我们考虑直接让 an=k*(b+1)-1 然后判断 s-an 是正数还是非正数,非正数就直接让 an=s,正数就让后面的所有ai 都变成 ai=k-1 并让 s-=k-1,直到 s<=k-1 让此时对应的 ai=s 即可,如果到了 a1 都没有把 s 减完,那就输出 -1 即可。

我猜有人说,那有没有可能我平均到前面每个 向下取整(ai/k) 这样算下来的比直接让 an 拉满的最后结果要多呢?其实不然,不论你怎么分配,他能给总和提供的价值是不变的,所以你让他先拉满和不让是同样的,甚至你平均下来的话空可能不够用导致出错。

代码示例      

#include
#include
#include
#include
#include
using namespace std;

#define int long long

const int maxn=100100;
int t;
int cnt;
int n,k,b,s;
int a[maxn];
bool flag;

signed main(){
    cin>>t;
    while(t--){
        cin>>n>>k>>b>>s;
        if(k*b>s){
            cout<<"-1"<=k*(b+1)-1){
                    s-=k*(b+1)-1;
                    ok=1;
                    a[++cnt]=k*(b+1)-1;    
                }
                else{
                    a[++cnt]=s;
                    s=0;
                }
            } 
            else{
                if(s>=k-1){
                    s-=k-1;
                    a[++cnt]=k-1;    
                } 
                else{
                    a[++cnt]=s;
                    s=0;
                }
            }
        }
        if(flag==0){
            if(cnt!=n) for(int i=cnt+1;i<=n;i++) a[i]=0;
            for(int i=n;i>=1;i--) cout<

C. Monoblock

题目大意

给你一个数组,长度为 n,连续相同的一段区间可以视为一个数,让你求出其所有连续子区间的个数和,同时会给出一组 i和x 也就是将数组位置的 i 变成 x,再重新求一次连续子区间的个数和。

解题思路

(n-(i+1)+1)*i,这是解题的关键,也就是说当 a[i]!=a[i+1] 时他能提供的价值为 (n-(i+1)+1)*i。让我们把数组中存在两个相邻的不同数字的地方称为“接点”。现在从这些接点的角度考虑这个问题:如果 f(接点) 等于重叠这个接点的段数,那么所有子段上能提供的价值之和等于所有接点上的 f(接点)之和。由此导出了除了更改请求之外的解决方案:在数组上迭代,找到“关节”,比较相邻的数字,如果ai不同于ai+1,我们必须在答案上加上i*(n-i),即从左边开始的子段的可能的开始数乘以从右边开始的可能的结束数。我们应该如何应用更改?事实上,只要检查改变数字的位置是否有任何相邻的关节,减去这些关节重叠的子段的数量,然后在为数字设置新值后进行类似的操作就可以了。

代码示例

#include
#include
#include
#include
#include
#include
using namespace std;

#define int long long

int n,m;
int ans=0;

signed main() {
    cin>>n>>m;
    vectora(n+2,0);
    for(int i=1;i<=n;++i) cin>>a[i];
    for(int i=1;i<=n;++i) ans+=(a[i]!=a[i+1])*(n-(i+1)+1)*i;
    while(m--){
        int i, x;
        cin>>i>>x;
        ans-=(a[i]!=a[i-1])*(n-i+1)*(i-1);
        ans-=(a[i+1]!=a[i])*(n-(i+1)+1)*i;
        a[i]=x;
        ans+=(a[i]!=a[i-1])*(n-i+1)*(i-1);
        ans+=(a[i+1]!=a[i])*(n-(i+1)+1)*i;
        cout<

D. 2+ doors

题目大意

给你一个空数组长度为 n,让你进行 q 次 ai|aj=x 的操作,问你最小的 a 数组是什么(在 a 和 b 不同的第一位置,数组 a 的元素比 b 中的相应元素小)。

解题思路

首先我们考虑一点一点单独的解决这个问题,因为这里的操作都是“位独立的”,一个特定幂的位不影响其他位,我们可以分别组合每一位的解。

代码示例

你可能感兴趣的:(题解,算法)