1. 程式人生 > >Codeforces Round #529 (Div. 3) 題解

Codeforces Round #529 (Div. 3) 題解

用兩個 次方 題意 技術分享 序列 put enc its 劃分

題目總鏈接: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) 題解