Codeforces Round #772 (Div. 2)(ABCDE)

Codeforces Round #772 (Div. 2)(ABCDE)

A. Min Or Sum
在这里插入图片描述
题意:给一个长度为n的序列,可以多次进行操作,每次选择不同的i,j,使得满足 a [ i ] ∣ a [ j ] = x ∣ y a[i]|a[j]=x|y a[i]a[j]=xy,x、y任意,问最后序列的最小总和。
思路:因为可以一直或操作 a [ i ] ∣ a [ j ] = x ∣ y a[i]|a[j]=x|y a[i]a[j]=xy也可以看成为变化后 a [ i ] + a [ j ] a[i]+a[j] a[i]+a[j]的值。所以我们只要看所有数在二进制下的哪些位是1就行了,最后加起来就是答案

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=105;
ll n,sum,bit[35];

void solve(){
    sum=0;
    for(ll i=0;i<=30;i++) bit[i]=0;
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++){
        ll x;scanf("%lld",&x);
        for(ll j=0;j<=30;j++){
            ll y=(x>>j)&1;
            if(y==1) bit[j]++;
        }
    }
    for(ll i=0;i<=30;i++){
        if(bit[i]!=0) sum+=(1ll<<i);
    }
    printf("%lld\n",sum);
}

int main(){
    int t;scanf("%d",&t);
    while(t--) solve();
}

B. Avoid Local Maximums
在这里插入图片描述
题意:给你一个长度为n的序列,问最少经过多少次变换,保证没有局部最大值。局部最大值定义为严格大于两边的值。所有第一位和最后一位一定不能成为局部最大值。要求输出最少次数和变化后的序列。
思路:我们从2遍历到n-1,如果第i个数满足局部最大值的条件,我们就去变化i+1位置的数,这样变化最少。
变化最好是可以使得位置i、i+1、i+2当不能成为局部最大值。所以a[i+1]=max(a[i],a[i+2]。
(我初始加了边界 a [ n + 1 ] = 1 e 9 a[n+1]=1e9 a[n+1]=1e9,可以不加)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,sum,a[N];

void solve(){
    sum=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    a[n+1]=1e9;
    for(int i=2;i<n;i++){
        if(a[i]>a[i-1]&&a[i]>a[i+1]){
            sum++;
            a[i+1]=max(a[i],a[i+2]);
        }
    }
    printf("%d\n",sum);
    for(int i=1;i<=n;i++) printf("%d%c",a[i],(i==n)?'\n':' ');
}

int main(){
    int t;scanf("%d",&t);
    while(t--) solve();
}

C. Differential Sorting
在这里插入图片描述
题意:给一个长度为n的序列,可以进行m次操作 ( 0 < = m < = n ) (0<=m<=n) (0<=m<=n),每次操作选定一个三元组{x,y,z}, ( 1 < = x < y < z < = n ) (1<=x<y<z<=n) (1<=x<y<z<=n),将 a [ x ] = a [ y ] − a [ z ] a[x]=a[y]-a[z] a[x]=a[y]a[z]。问是否可以最终将序列变成一个非降序列。不能输出-1,可以输出操作数和具体操作方案
思路:首先,必须保证 a [ n − 1 ] < = a [ n ] a[n-1]<=a[n] a[n1]<=a[n],因为这两处的值无法更改,让从后往前遍历,找到第一个非负数(位置idx),因为一个数减去一个负数,会使值增加,我们找到非负数,遍历时发现 a [ i ] > a [ i + 1 ] a[i]>a[i+1] a[i]>a[i+1],我们就可以选择{i,i+1,idx},这样就可以使得 a [ i ] < = a [ i + 1 ] a[i]<=a[i+1] a[i]<=a[i+1]

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,a[N];
struct node{int x,y,z;};
vector<node>ans;

void solve(){
    ans.clear();
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    if(a[n]<a[n-1]){puts("-1");return;}
    int idx=-1;
    if(a[n]>=0) idx=n;
    else if(a[n-1]>=0) idx=n-1;
    for(int i=n-2;i>=1;i--){
        if(a[i]<=a[i+1]) continue;
        else{
            if(idx==-1) {puts("-1");return;}
            ans.push_back({i,i+1,idx});
            a[i]=a[i+1]-a[idx];
        }
    }
    int len=ans.size();
    printf("%lld\n",len);
    for(int i=0;i<len;i++) printf("%lld %lld %lld\n",ans[i].x,ans[i].y,ans[i].z);
}

signed main(){
    int t;scanf("%lld",&t);
    while(t--) solve();
}

D. Infinite Set在这里插入图片描述
题意:给定n个不同的数,可以进行两种操作,就所有出现的数放入一个set,问值小于 2 p 2^p 2p的元素个数,答案 m o d 1 e 9 + 7 mod1e9+7 mod1e9+7
思路:

/*
    看做二进制数
    操作1:x*2+1 相当于 x1 加一位1
    操作2:x*4 相当于x00 加两位0
    并且任何数y都只可能由一种数转移而来不存在有两个数x1,x2进行若干次操作都可以得到y(x1,x2不能互相得到)
    所以长度为len的二进制数可以由len-1的数进行操作1得到,由长度为len-2的数进行操作2得到,不会重复计算
    另外有一些数不可能由以上操作得到 即 x为偶数且不是4的倍数,只能作为ai出现在集合里
    或者能衍生出它的数不存在ai中,这些数单独计算
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e5+5;
const ll mod=1e9+7;
ll n,p,a[N],dp[N];
map<int,bool>mp;

int main(){
    scanf("%lld%lld",&n,&p);
    for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    for(ll i=1;i<=n;i++){
        ll now=a[i];
        while(1){
            if(now==1) break;
            if(mp[now]) break;
            if(now%2==1) now/=2;
            else if(now%4==0) now/=4;
            else break;
        }
        if(mp[now]) continue;
        else{
            mp[a[i]]=true;
            ll len=0;
            while((1ll<<len)<a[i]) len++;
            if((1ll<<len)==a[i]) dp[len+1]++;
            else dp[len]++;
        }
    }
    for(ll i=1;i<=p;i++) dp[i]=(dp[i]+dp[i-1]+dp[i-2])%mod;
    ll sum=0;
    for(ll i=1;i<=p;i++) sum=(sum+dp[i])%mod;
    printf("%lld\n",sum);
}

E. Cars
在这里插入图片描述题意和思路在代码块内

//在水平线上有n辆车。有m种限制,每种限制为{type,x,y}
//当type==1时,第x辆车和第y辆车不能相遇
//当type==2时,第x辆车和第y辆车必须相遇一次
//问是否有方式,确定小车方向后,以及初始小车位置,使得满足所有限制
//先二分图匹配看是否存在方式,同时用point记录点的关系信息。point[i]存的都是必须在第i辆车右边的车的编号
//因为速度任意,所有不管是否相遇,方向都要相反
//col[i]==1:第i辆车向右 --->
//col[i]==0:第i辆车向左 <---

/*注释①
col[u]==1&&type==2  
第u辆车方向向右(--->),此时第v辆车必须和第u辆车相遇,且已知第v辆车方向向左(<---)
那么第v辆车一定是在第u辆车的右边。 情况(--u->  <-v--)

col[u]==0&&type==1
第u辆车方向向左(<---),此时第v辆车必须和第u辆车不能相遇,且已知第v辆车方向向右(--->)
那么第v辆车一定是在第u辆车的右边。 情况(<-u--  --v->)

*/

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,col[N],pos[N],in[N],now;
bool vis[N],flag=true;
vector<int>point[N],nodes;
vector<pair<int,int> >edge[N];

void dfs1(int u,int color){
    if(!flag) return;
    nodes.push_back(u);
    col[u]=color;
    int len=edge[u].size();
    for(int i=0;i<len;i++){
        int type=edge[u][i].first;
        int v=edge[u][i].second;
        if(col[u]==col[v]) {flag=false;return;}
        if((col[u]==1&&type==2)||(col[u]==0&&type==1)) point[u].push_back(v); //上面注释①
        else point[v].push_back(u);
        if(col[v]==-1) dfs1(v,color^1);
    }
}

void dfs2(int u){
    if(!flag) return;
    in[u]=1;vis[u]=true;
    int len=point[u].size();
    for(int i=0;i<len;i++){
        int v=point[u][i];
        if(in[v]){flag=false;return;}//满足条件则说明,本该出现在u左边的v已经提前出现
        if(!vis[v]) dfs2(v);
    }
    in[u]=0;
    pos[u]=now--;
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) col[i]=-1;
    for(int i=1;i<=m;i++){
        int type,x,y;scanf("%d%d%d",&type,&x,&y);
        edge[x].push_back({type,y});
        edge[y].push_back({type,x});
    }
    for(int i=1;i<=n;i++){
        if(col[i]==-1){//可能分不同关系连通块
            nodes.clear();
            dfs1(i,1);
            int len=nodes.size();
            for(int j=0;j<len;j++){
                if(!vis[nodes[j]]) dfs2(nodes[j]);
            }
        }
        if(!flag) break;
    }
    if(!flag) puts("NO");
    else{
        puts("YES");
        for(int i=1;i<=n;i++){
            if(col[i]==1) printf("R %d\n",pos[i]);
            else printf("L %d\n",pos[i]);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值