码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces Round #529 (Div. 3) 题解

时间:2019-01-02 01:23:52      阅读:143      评论:0      收藏:0      [点我收藏+]

标签:hide   次方   数组   most   思考   hid   假设   ORC   需要   

题目总链接:https://codeforces.com/contest/1095

A. Repeating Cipher

题意:

给出一个字符串,然后将其“收缩”,不断地将连续的一个、两个只变为一个,问最终的字符串是什么。

 

题解:

模拟一下就好了。

 

代码如下:

技术分享图片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105;
int n;
char s[N];
int main(){
    scanf("%d",&n);
    scanf("%s",s);
    int now = 1;
    int last = 0;
    for(int i=0;i<n;i++){
        if(i-last+1==now){
            now++;
            last = i;
            cout<<s[i];
        }
    }
    return 0;
}
View Code

 

B. Array Stabilization

题意:

从n个数中去掉一个,使得最大减最小最小。

 

题解:

排个序就好了。

 

代码如下:

技术分享图片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+5;
int a[N];
int n;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1);
    cout<<min(a[n-1]-a[1],a[n]-a[2]);
    return 0;
}
View Code

 

C. Powers Of Two

题意:

给出n个数,问能否将其化为k个2^xi次方的和,如果可以,输出这k个数。

 

题解:

对于2^xi,我们最多可以将其划分为2^xi个1加起来,我们可以通过这个作为上界来判断可行性。

然后对于n进行划分,找出n是由哪些2的次方加起来的。

对于一个2^xi,我们可以将其划分为2^(xi-1)+2^(xi-1),观察这里可以发现,每次将一个指数减半(除开0)可以多得到一个数。

然后不断模拟这个操作就好了。这里可以用multiset比较方便。

 

代码如下:

技术分享图片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k;
multiset <int> s;
int main(){
    cin>>n>>k;
    for(int i=30;i>=0;i--){
        if(n&(1<<i)) s.insert(i);
    }
    int len = s.size();
    if(n<k || len>k){
        puts("NO");
        return 0;
    }
    puts("YES");
    while(s.size()<k){
        auto it = s.end();
        it--;
        int now=*it;
        s.erase(it);
        s.insert(now-1);
        s.insert(now-1);
    }
    for(auto x:s)
        cout<<(1<<x)<<" ";
    return 0;
}
View Code

 

D. Circular Dance

题意:

给出一个环,现在知道每个位置的后两个位置是哪两个人(但位置不能确定),问这些人是怎么排列的。

 

题解:

根据一个人的后两个人的信息以及后两个人的后两个人信息,可以确定出三个人的位置,然后就模拟一下这个过程就好啦。

 

代码如下:

技术分享图片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+5;
int a[N][5];
int n;
int vis[N];
vector <int> ans;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d%d",&a[i][0],&a[i][1]);
    ans.push_back(1);
    vis[1]=1;
    for(int i=1;i<=n;i++){
        int p1=a[i][0],p2=a[i][1];
        if(vis[p1]&&vis[p2])break ;
        vis[p1]=vis[p2]=1;
        if(a[p1][0]==p2 || a[p1][1]==p2){
            ans.push_back(p1);
            ans.push_back(p2);
            i=p2-1;
        }else{
            ans.push_back(p2);
            ans.push_back(p1);
            i=p1-1;
        }
    }
    for(int i=0;i<n;i++) cout<<ans[i]<<" ";
    return 0;
}
View Code

 

E. Almost Regular Bracket Sequence

题意:

给出一个括号序列,现在每次可以改变一个位置的括号方向,问可以改变多少次不同位置的括号使这个括号变成一个合法的括号。

合法括号的定义为:在里面加上1 or "+"过后,这是个合法的式子。

 

题解:

假设我们当前改变第i个位置,那么我们肯定需要前面以及后面的括号信息。

第i个位置括号的改变有两种,我们分析一种就是了,另外一种都差不多,现在就假设第i个括号为"(",我们分析它是否可以变为")"。

首先要知道的是,i位置前面的括号以及i这个位置的括号能够“合并”,以及i位置后面的括号能够“合并”,并且1~i-1要多一个"("。

1~x的括号能够合并的充要条件是合并后不存在")",那么我们可以用一个数组记录下前缀和,1代表"(",-1代表")",当l[i]<0时,则说明i以及之后的位置的左边都不能够合并了,那么这时对这些位置的修改就没意义了,最终也不能成为一个合法式子;r[i]也同理,预处理一个后缀和就行了。

最后再用两个数组来记录一下前缀和、后缀和,用来判断是否要多个1。

 

代码如下:

技术分享图片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
int n;
char s[N];
int l[N],r[N],dl[N],dr[N];
int main(){
    scanf("%d",&n);
    scanf("%s",s+1);
    int d=0;
    l[0]=1;r[n+1]=1;
    for(int i=1;i<=n;i++){
        if(s[i]==() d++;
        else d--;
        if(d<0) l[i]=0;
        else l[i]=l[i-1];
        dl[i]=d;
    }
    d=0;
    for(int i=n;i>=1;i--){
        if(s[i]==)) d++;
        else d--;
        if(d<0) r[i]=0;
        else r[i]=r[i+1];
        dr[i]=d;
    }
    int ans = 0;
    for(int i=1;i<=n;i++){
        if(l[i-1]&&r[i+1]){
            int now = dl[i-1]-dr[i+1];
            if(s[i]==( && now==1) ans++;
            else if(s[i]==) && now==-1) ans++;
        }
    }
    cout<<ans<<endl;
    return 0;
}
View Code

 

F. Make It Connected

题意:

给出n个点,每个点没有边相连,但是都有一个权值,边权为两端点的权值和;

然后给出m条边及其权值,其含义为,这条边连接的两个点的边权可以为给出的权值。

最后求怎么连接各点使得比边权和最小。

 

题解:

因为要是各点连通,所以就是求一个生成树,但是把所有边构建出来然后跑MST太麻烦了。

思考一下就可以发现我们只需要点权最小的点与其他点相连的边以及给出来的边就行了,这样跑MST可以得到最优解。

最终只需要4e5条边。

 

代码如下:

技术分享图片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+5 ;
int n,m,pos,tot;
ll a[N];
struct Edge{
    int u,v;
    ll w;
    bool operator < (const Edge &A)const{
        return w<A.w;
    }
}e[N<<2];
int f[N];
int find(int x){
    return f[x]==x ? f[x] : f[x]=find(f[x]);
}
int main(){
    scanf("%d%d",&n,&m);
    ll mx = 1e18;
    for(int i=1;i<=n;i++){
        scanf("%I64d",&a[i]);
        if(a[i]<mx){
            mx=a[i];
            pos=i;
        }
    }
    for(int i=1;i<=m;i++){
        int u,v;ll w;
        scanf("%d%d%I64d",&u,&v,&w);
        e[++tot].u=u;e[tot].v=v;e[tot].w=w;
    }
    for(int i=1;i<=n;i++){
        if(i==pos) continue ;
        e[++tot].u=i;e[tot].v=pos;
        e[tot].w=a[i]+a[pos];
    }
    for(int i=0;i<=n+1;i++) f[i]=i;
    sort(e+1,e+tot+1);
    ll ans = 0;
    for(int i=1;i<=tot;i++){
        int u=e[i].u,v=e[i].v;
        ll w = e[i].w;
        int fx=find(u),fy=find(v);
        if(fx==fy) continue ;
        else{
            f[fx]=fy;
            ans+=e[i].w;
        }
    }
    cout<<ans<<endl;
    return 0;
}
View Code

 

Codeforces Round #529 (Div. 3) 题解

标签:hide   次方   数组   most   思考   hid   假设   ORC   需要   

原文地址:https://www.cnblogs.com/heyuhhh/p/10206633.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!