AtCoder Beginner Contest 177 題解


AtCoder Beginner Contest 177 題解

A - Don't be late

問你能不能在時間\(T\)內用不高於\(S\)的速度走過\(D\)的路程,轉化為判斷\(S\times T\)不小於\(D\)即可。

#include<bits/stdc++.h>
using namespace std;

int d,s,t;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>d>>t>>s;
    cout<<(t*s>=d?"Yes":"No");

    return 0;
}

B - Substring

給你兩個字符串\(S\)\(T\),保證\(S\)不短於\(T\)。問你在\(S\)中任意截取一個長度和\(T\)相同的子串(子串指原字符串中一段連續的字符,特別地,空串和原字符串也是原字符串的子串),在這個字符串中最少要修改多少個字母才能和\(T\)相同。

枚舉截取的字符串位置即可。

#include<bits/stdc++.h>
using namespace std;

string s,t;
int n,m,a=1e9;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>s>>t;
    n=s.size();m=t.size();
    for(int i=0;i+m<=n;i++){
        int c=0;
        for(int j=0;j<m;j++)c+=s[i+j]!=t[j];
        a=min(a,c);
    }
    cout<<a<<endl;

    return 0;
}

C - Sum of product of pairs

給你一個序列,要求你把其中的數字兩兩相乘,輸出得到的所有乘積的和。

我們從序列中從前到后枚舉一個數字,把它和前面的所有數分別相乘,並把這些乘積加到答案里,就可以不重復的算出答案(此時一種數對的相乘結果只被計算了一次)。根據乘法分配律,這相當於乘上前面所有數的和,然后就可以維護一個前綴和,加速求解。

#include<bits/stdc++.h>
using namespace std;

const int mod=1e9+7;

int n,a,ans,pre;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a;
        ans=(ans+(long long)pre*a)%mod;
        pre=(pre+a)%mod;
    }
    cout<<ans<<endl;

    return 0;
}

D - Friends

給你一張無向圖,要求划分成一些集合,使得集合內的點兩兩不能達到。輸出最小集合數。

首先可以證明集合數的數量不少於最大的連通塊的點的個數,因為每個點需要被單獨地划分出來。

然后可以證明這些集合足夠存放所有的點,因為不同連通塊之內的點可以被放進同一個集合里,因此可以把它們相互獨立地考慮。而一個連通塊需要的集合數不會超過最大的連通塊需要的集合數。

#include<bits/stdc++.h>
using namespace std;

int n,m;
vector<int> g[200005];
int sz;
bool vis[200005];

void dfs(int x){
    vis[x]=1;
    sz++;
    for(int &y:g[x])if(!vis[y]){
        dfs(y);
    }
}

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n>>m;
    for(int i=1;i<=m;i++){
        static int u,v;
        cin>>u>>v;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
    }
    int ans=0;
    for(int i=1;i<=n;i++)if(!vis[i]){
        sz=0;
        dfs(i);
        ans=max(ans,sz);
    }
    cout<<ans<<endl;

    return 0;
}

E - Coprime

給你一個整數序列,判斷是否兩兩互質,整個序列互質(整個序列的公因數為\(1\)),或者不滿足先前兩種情況。

首先先判斷是否整個序列互質,這個單獨判斷方便一點,就求出整個序列的最大公因數。

那么再判斷是否兩兩互質,我們對每個數分解質因數,記錄它包含了哪些質因子(不論那個質因子被乘了多少次)。假如有多於一個的數包含同一個質因子,那么這個序列就不是兩兩互質的。

#include<bits/stdc++.h>
using namespace std;

inline int gcd(int a,int b){
    while(b){
        a=a%b;
        swap(a,b);
    }
    return a;
}

int n,a[1000005],u[1000005];

int main(){

    scanf("%d",&n);
    int g=0;
    for(int i=1;i<=n;i++){
        scanf("%d",a+i);
        g=gcd(g,a[i]);
        for(int j=2;j*j<=a[i];j++){
            if(a[i]%j==0){
                u[j]++;
                while(a[i]%j==0)a[i]/=j;
            }
        }
        u[a[i]]++;
    }
    if(g>1){
        cout<<"not coprime\n";
        return 0;
    }
    for(int i=2;i<=(int)1e6;i++){
        if(u[i]>1){
            cout<<"setwise coprime\n";
            return 0;
        }
    }
    cout<<"pairwise coprime\n";


    return 0;
}

F - I hate Shortest Path Problem

給你一個高\(H+1\),寬\(W\)的矩形,第\(i\)行第\(A_i\)個位置到第\(B_i\)個位置下方有擋板,特別地,底部的一行下方沒有擋板。

你可以從頂部一行的任意一個位置開始行動,每一步可以向下或向右走一格,要求不能走出矩形或者穿過擋板。問你走到第\(2, 3, \dots, H\)行各最少需要多少步,無法達到輸出-1

我們首先需要觀察到,除了起始位置的選擇不一定盡量靠前外,否則能向下走就直接向下走,不然向右走一格,再嘗試向下走,這么持續下去一定是最優的一種考慮。

我們可以模擬走到某一行某一個位置最少需要多少步。

為了維護方便,我們反向考慮如何走到這個格子。這個格子一定是上一個格子向右移動了某些步數(可能移動零步),然后向下移動一格走到的。向下移動后,我們不需要考慮再向右移動的情況了。因為在轉移到下一行時,之前描述的“向右移動了某些步數,然后向下移動一格”包含了這個情況。同時這樣也可以保證對此行答案的貢獻最優。

我們可以考慮使用數據結構來優化。那么此時我們需要分類一下:

  1. 對於上方沒有擋板的格子,我們可以直接從上向下轉移,假如這個走法不是最優的,可以證明它對答案沒有影響。
  2. 對於上方有擋板的格子,直接把它的答案設成正無窮大,因為無法走到。假如這個走法不是最優的,也可以證明它對答案沒有影響。
  3. 對於左上方有擋板的格子,可以從上方的格子所有左邊的格子轉移來。

感性理解,每一個格子繼續行動時,它的貢獻一定會通過以上三種轉移方式繼續向下傳下去,所以可以保證是最優的。

然后我們就可以使用兩棵線段樹維護了,假如我們定義當前行的位置\(i\)的答案為\(ans_i\),一棵維護真實的最小值\(ans_i\),一棵維護\(ans_i+W-i\),用於做第三種更新。需要先做第三種更新,因為我們此時的轉移都做在同一個線段樹內,順序會相互影響。

#include<bits/stdc++.h>
using namespace std;

struct SegTree{

    int sz;
    vector<long long> dat,laz;

    SegTree(){
        sz=1<<18;
        dat.resize(sz<<1);
        laz.resize(sz<<1);
    };

    void upd(int id,int l,int r,int ql,int qr,long long val){
        if(qr<l||r<ql)return;
        if(ql<=l&&r<=qr){
            laz[id]+=val;
            dat[id]+=val;
            return;
        }
        laz[id<<1]+=laz[id];
        dat[id<<1]+=laz[id];
        laz[id<<1|1]+=laz[id];
        dat[id<<1|1]+=laz[id];
        laz[id]=0;
        upd(id<<1,l,l+r>>1,ql,qr,val);
        upd(id<<1|1,(l+r>>1)+1,r,ql,qr,val);
        dat[id]=min(dat[id<<1],dat[id<<1|1]);
    }

    long long qry(int id,int l,int r,int ql,int qr){
        if(qr<l||r<ql)return 1e18;
        if(ql<=l&&r<=qr){
            return dat[id];
        }
        laz[id<<1]+=laz[id];
        dat[id<<1]+=laz[id];
        laz[id<<1|1]+=laz[id];
        dat[id<<1|1]+=laz[id];
        laz[id]=0;
        return min(qry(id<<1,l,l+r>>1,ql,qr),qry(id<<1|1,(l+r>>1)+1,r,ql,qr));
    }

    void upd(int l,int r,long long d){
        upd(1,1,sz,l,r,d);
    }

    long long qry(int l,int r){
        return qry(1,1,sz,l,r);
    }
};

int n,m;
int a[200005],b[200005];
SegTree upd,qry;

int main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n>>m;
    for(int i=1;i<=m;i++)upd.upd(i,i,m-i);
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i];
        qry.upd(b[i]+1,b[i]+1,min(upd.qry(1,b[i])-(m-b[i]-1)-qry.qry(b[i]+1,b[i]+1),0ll));
        upd.upd(b[i]+1,b[i]+1,min(upd.qry(1,b[i])-upd.qry(b[i]+1,b[i]+1),0ll));
        qry.upd(a[i],b[i],1e9);
        upd.upd(a[i],b[i],1e9);
        qry.upd(1,m,1);
        upd.upd(1,m,1);
        if(qry.qry(1,m)>=1e9)cout<<"-1\n";
        else cout<<qry.qry(1,m)<<'\n';
    }

    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM