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

Codeforces Round #535 (Div. 3) 題解

計算 perm () distinct 應該 arr get space 直接

Codeforces Round #535 (Div. 3)

題目總鏈接:https://codeforces.com/contest/1108

太懶了啊~好久之前的我現在才更新,趕緊補上吧,不能漏掉了。

A. Two distinct points

題意:

給出兩個區間的左右邊界,輸出兩個數,滿足兩個數分別在兩個區間內且這兩個數不相等。

題解:

直接輸出左端點然後判斷一下就行了。

代碼如下:

技術分享圖片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int q;
int l1,r1,l2,r2;
int main(){ cin>>q; for(int i=1;i<=q;i++){ cin>>l1>>r1>>l2>>r2; if(l1==l2) l2++; cout<<l1<<" "<<l2<<endl; } return 0; }
View Code

B. Divisors of Two Integers

題意:

給出n個數,這n個數都為兩個數的因子,最後要求你輸出這兩個數為什麽。

題解:

既然是因子,那麽答案也肯定包含在這n個數中,並且因子應該是較大的數。

那麽我們排序後從後往前枚舉,遇到一個沒有被刪除的數我們就把它當作我們的答案,並且在其它數中刪掉它的因子。

代碼如下:

技術分享圖片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200;
int n;
int d[N],del[N];
vector <int> vec[N];
int main(){
    scanf("%d",&n);
    
for(int i=1;i<=n;i++) scanf("%d",&d[i]); sort(d+1,d+n+1); for(int i=1;i<=n;i++){ int x=d[i]; for(int j=2;j*j<=x;j++){ if(x%j==0){ vec[i].push_back(j); if(j*j!=x) vec[i].push_back(x/j); } } } for(int i=n;i>=1;i--){ if(del[i]) continue ; int cnt = 0; while(cnt<vec[i].size()){ for(int j=2;j<=i;j++){ if(del[j]) continue ; if(d[j]==vec[i][cnt]){ cnt++; del[j]=1; break ; } } } } int tot=0; for(int i=3;i<=n;i++) if(!del[i]) tot++; int now = 0; int x=1,y=1; for(int i=3;i<=n;i++){ if(now==tot-1){ if(!del[i]) y*=d[i]; } else if(!del[i]) x*=d[i],now++; } cout<<x<<" "<<y; return 0; }
View Code

C. Nice Garland

題意:

給出一個字符串,這個字符串只由三個大寫字母"B","G","R"構成,然後問你最少需要修改多少個字符,可以使得這個串成為一個“好串”。

好串的定義是,對於每一個字符,它和其它相同字符位置相差都為3的倍數。

題解:

因為相差都為3的倍數,那麽我們可以斷定前三個字符是互不相同的,並且之後每三個字符也是互不相同的。

另外也可以知道,後面的位置與前三個的位置是一一對應的,這樣才滿足相差為3的倍數這個條件。

那麽我們就枚舉前三個的所有可能情況,然後根據這個去統計後面需要的修改次數,最後取最小值就行了。

直接枚舉太麻煩,所以使用全排列函數next_permutation。

代碼如下:

技術分享圖片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+5;
int n;
char s[N],a[N];
char p[10]={R,B,G};
int main(){
    scanf("%d",&n);
    scanf("%s",s);
    int ans = 0;
    sort(p,p+3);
    ans=0x3f3f3f3f3f;
    int tot=0;
    do{
            tot++;
       int cnt = 0;
       for(int i=0;i<min(n,3);i++){
            if(p[i]!=s[i]) cnt++;
       }
       for(int i=3;i<n;i++){
            if(p[i%3]!=s[i]) cnt++;
       }
       if(ans>cnt){
        ans=cnt;
        for(int i=0;i<3;i++) a[i]=p[i];
       }
    }while(next_permutation(p,p+3));
    cout<<ans<<endl;
    for(int i=0;i<n;i++) cout<<a[i%3];
    return 0;
}
View Code

D. Diverse Garland

題意:

還是給出一個只由"R","G","B"構成的字符串,現在問最少的修改次數使得兩兩相鄰的字符不想等為多少。

題解:

這題可以dp吧,但是我是直接貪心來做的。

直接這樣想,對於一段連續且相等的串,我們只需要改變其偶數位置就行了。註意一下改變的時候不要和前面以及後面的相等了。

代碼如下:

技術分享圖片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+5;
int n;
char s[N],p[N];
char a[N]={R,B,G};
void change(int x){
    for(int i=0;i<3;i++){
        p[x]=a[i];
        if(p[x]!=s[x-1] && p[x]!=s[x+1]) return ;
    }
    return ;
}
int main(){
    scanf("%d",&n);
    scanf("%s",s+1);
    int ans = 0;
    int tot=0;
    for(int i=1;i<=n;i++) p[i]=s[i];
    for(int i=1;i<=n;i++){
        if(s[i]!=s[i-1]) tot=1;
        else{
            tot++;
            if(tot%2==0){
                change(i);
                ans++;
            }
        }
    }
    cout<<ans<<endl;
    for(int i=1;i<=n;i++) cout<<p[i];
    return 0;
}
View Code

E1. Array and Segments (Easy version)

題意:

給出n個數ai以及m個區間,現在要求你選擇一些區間,然後每個區間裏面的所有數減去1,最後問選擇區間過後,n個數中最大值減去最小值最大為多少。

題解:

簡單版本的數據範圍比較小,直接暴力就行了。

暴力的話考慮枚舉最後最小的位置是哪一個,然後就選擇所有覆蓋這個點的區間,暴力計算就ok。

代碼如下:

技術分享圖片
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 305,M = 305;
int a[N],mx[N],mn[N],b[N];
struct seg{
    int l,r,id;
    bool operator < (const seg &A)const{
        return r<A.r;
    }
}p[M];
int n,m;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
    for(int i=1;i<=m;i++) scanf("%d%d",&p[i].l,&p[i].r),p[i].id=i;
    sort(p+1,p+m+1);
    memset(mn,INF,sizeof(mn));
    for(int i=1;i<=n+1;i++) mx[i]=-INF;
    for(int i=n;i>=1;i--)
        mx[i]=max(mx[i+1],a[i]);
    for(int i=n;i>=1;i--)
        mn[i]=min(mn[i+1],a[i]);
    int ans = mx[1]-mn[1];
    int num=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++) a[j]=b[j];
        for(int j=1;j<=m;j++){
            if(p[j].l<=i && p[j].r>=i){
                for(int k=p[j].l;k<=p[j].r;k++) a[k]--;
            }
        }
        int minx = INF,maxx = -INF;
        for(int j=1;j<=n;j++) minx=min(minx,a[j]),maxx=max(maxx,a[j]);
        if(ans<maxx-minx){
            ans=maxx-minx;
            num=i;
        }
    }
    cout<<ans<<endl;
    int tot=0;
    vector <int> vec;
    for(int i=1;i<=m;i++){
        if(p[i].l<=num && p[i].r>=num) tot++,vec.push_back(p[i].id);
    }
    cout<<tot<<endl;
    for(auto v:vec) cout<<v<<" ";
    return 0;
}
View Code

E2. Array and Segments (Hard version)

題意:

這題和E1的題目描述都是一樣的,不同的就是數據範圍變大了,現在是n<=1e5,m<=300,顯然不能直接暴力枚舉每個最小值了。

題解:

區間個數還是那麽多,註意到區間個數比n小很多,那麽我們可以考慮離散化,這樣數軸上最多只剩下600個點,然後我們記錄一下每一段的最大最小值。

這樣段數也只有最多600個,區間還是300個,暴力枚舉就不會超時了。

為啥可以把每一段看成點呢?因為對於被區間“分隔”的每一段區間,它們所擁有的區間覆蓋都是相同的,這個畫下圖就知道了。

給出代碼:

技術分享圖片
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e5+5,M = 305;
int a[N],l[N],r[N],b[N],c[N];
int n,m,tot;
int mx[N],mn[N];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&l[i],&r[i]);
        r[i]++;
        b[++tot]=l[i];
        b[++tot]=r[i];
    }
    b[++tot]=1;b[++tot]=n+1;
    sort(b+1,b+tot+1);
    tot = unique(b+1,b+tot+1)-(b+1);
    for(int i=1;i<=m;i++){
        l[i]=lower_bound(b+1,b+tot+1,l[i])-b;
        r[i]=lower_bound(b+1,b+tot+1,r[i])-b;
    }
    int t1=-INF,t2=INF;
    for(int i=1;i<tot;i++){
        mx[i]=mn[i]=a[b[i]];
        for(int j=b[i]+1;j<b[i+1];j++){
            mx[i]=max(mx[i],a[j]);
            mn[i]=min(mn[i],a[j]);
        }
        t1=max(t1,mx[i]);t2=min(t2,mn[i]);
    }
    vector <int> ans[N];
    int Ans=t1-t2,pos=0;
    for(int x=1;x<tot;x++){
        memset(c,0,sizeof(c));
        for(int j=1;j<=m;j++){
            if(l[j]<=x && r[j]>x)
                ans[x].push_back(j);
        }
        if(!ans[x].size()) continue ;
        for(auto v:ans[x]){
            c[l[v]]--;c[r[v]]++;
        }
        int s = 0,minx = INF,t=0,maxx=-INF;
        for(int i=1;i<tot;i++){
            s+=c[i];
            if(!t || mn[i]+s<minx) minx=mn[t=i]+s;
        }
        s = 0;
        for(int i=1;i<tot;i++){
            s+=c[i];
            if(mx[i]+s>maxx) maxx=mx[i]+s;
        }
        if(maxx-minx>Ans){
            Ans=maxx-minx;
            pos=x;
        }
    }
    cout<<Ans<<endl<<ans[pos].size()<<endl;
    for(auto v:ans[pos]) cout<<v<<" ";
    return 0;
}
View Code

還有一種更巧妙地解法,用不上離散化,直接搞就是了。

就是還是從1~n進行枚舉,只有當前點為某個區間的端點時進行更新,然後再暴力求最大最小值來計算。

這個復雜度是O(n*m)的。

代碼如下:

技術分享圖片
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e5+5,M = 305;
int n,m;
int a[N],b[N],l[N],r[N];
vector <int> pl[N],pr[N];
int main(){
    cin>>n>>m;
    int mx = -INF ,mn= INF;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        mx=max(mx,a[i]);
        mn=min(mn,a[i]);
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&l[i],&r[i]);
        pl[l[i]].push_back(i);
        pr[r[i]+1].push_back(i);
    }
    int ans = mx-mn,pos=-1,tmp;
    for(int i=1;i<=n;i++){
        int len1=pl[i].size(),len2=pr[i].size();
        if(len1){
            for(auto v:pl[i]){
                for(int j=l[v];j<=r[v];j++) a[j]--;
            }
        }
        if(len2){
            for(auto v:pr[i]){
                for(int j=l[v];j<=r[v];j++) a[j]++;
            }
        }
        if(len1 || len2){
            mx = -INF,mn = INF;
            for(int i=1;i<=n;i++){
                mx=max(mx,a[i]);
                if(a[i]<mn){
                    mn=a[i];
                    tmp=i;
                }
                if(mx-mn>ans){
                    ans=mx-mn;
                    pos=tmp;
                }
            }
        }
    }
    cout<<ans<<endl;
    int cnt = 0;
    for(int i=1;i<=m;i++){
        if(l[i]<=pos && r[i]>=pos) cnt++;
    }
    cout<<cnt<<endl;
    for(int i=1;i<=m;i++){
        if(l[i]<=pos && r[i]>=pos) cout<<i<<" ";
    }
    return 0;
}
View Code

F. MST Unification

題意:

給出一個無向圖,每條邊有相應邊權,然後現在你可以對某些邊的邊權加上一個值。

現在問你最少需要多少次操作,可以使得這個圖的最小生成樹唯一。

題解:

這個思路還是挺巧妙的,也是求最小生成樹。我們假設目前邊權為x的邊有c條,那麽我們先剔除不用的邊a,再盡可能地在途中加邊b,最後的c-a-b條邊就是我們需要對其進行操作的邊。

然後稍微修改一下Kruskal就行了,具體見代碼吧:

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

這份題解總算寫完了~還有幾個找時間再補吧。

Codeforces Round #535 (Div. 3) 題解