Codeforces Round 1029 (Div. 3) A-G 题解

感觉题目出的很不错的一场div3

f题赛时看成了,a数组是从1到n的排列,想了半天是什么妙妙题目

A. False Alarm

找第一个和最后一个,关着的门的位置

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n,x;
    cin>>n>>x;

    vector<int> a(n+1);
    int p1=0,p2=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]){
            p2=i;
            if(!p1) p1=i;
        }
    }

    if(p1==0){
        cout<<"NO\n";
    }
    else{
        if(x>=p2-p1+1){
            cout<<"YES\n";
        }
        else cout<<"NO\n";
    }
}
    

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;
    while(ct--){
        solve();
    }

    return 0; 
}

B. Shrink

显然可以把最大的放在中间,例如 1 3 5 7 9 8 6 4 2

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    vector<int> a(n+1);

    int now=1;
    for(int l=1,r=n;l<=r;l++,r--){
        a[l]=now++;
        if(l==r) break;
        a[r]=now++;
    }

    for(int i=1;i<=n;i++){
        cout<<a[i]<<" ";
    }

    cout<<endl;
}
    

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;
    while(ct--){
        solve();
    }

    return 0; 
}

C. Cool Partition

从前往后贪心,只要当前这一段里的数,能够覆盖掉上一段中所有出现的数,就可以切一刀,把当前这一段切下来,过程中可以用map或set维护当前段和上一段出现的值

这里的覆盖指,上一段出现的所有数,在当前段都出现过

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    int ans=0;
    map<int,int> mp,tmp;

    vector<int> a(n+10);

    for(int i=1;i<=n;i++){
        cin>>a[i];
        tmp[a[i]]=1;
        if(mp.count(a[i])){
            mp.erase(a[i]);
        }
        if(mp.size()==0){
            ans++;
            mp=tmp;
            tmp.clear();
        }
    }

    cout<<ans<<endl;
}
    

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;
    while(ct--){
        solve();
    }

    return 0; 
}

D. Retaliation

从数组全0开始反推,假设进行了x次操作一和y次操作二,且x>y,则

可以把x分成:y+ (x-y)

此时操作可以看成,y次操作一和操作二,以及(x-y)次操作一

y次操作一和操作二后,数组的值一定相同,值一定是 y*(1+n)

剩下的操作一定是操作一。

因为此时数组的值相同,且进行的全是操作1,则数组一定是递增的。

此时可以得到结论,如果数组不是有序的,则一定非法

如果数组是递减的,说明操作二的数量更多,为了统一,可以reverse数组,把递减变成递增,交换操作二和操作一的次数。

此时,因为每次操作一,都会让相邻的两个数差值增加1,所以,数组相邻两个数的差值一定相同,且操作一的次数就是这个差值(因为每次操作一就会让差值+1)

设差值为val,val就是(x-y)

此时,可以让每个位置,都减去 i * val, 也就是(x-y)次操作一产生的数值

此时,数组里的每个数应该相同,且是(1+n)的倍数。否则非法

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    vector<int> a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }

    if(!is_sorted(a.begin()+1,a.end())){
        for(int l=1,r=n;l<=r;l++,r--){
            swap(a[l],a[r]);
        }
        if(!is_sorted(a.begin()+1,a.end())){
            cout<<"NO\n";
            return;
        }
        
    }

    sort(a.begin()+1,a.end());

    int val=a[2]-a[1];
    
    for(int i=2;i<=n;i++){
        if(a[i]-a[i-1]!=val){
            cout<<"NO\n";
            return;
        }
    }

    //val为公差的等差数列
    for(int i=1;i<=n;i++){
        a[i]-=i*val;
        if(a[i]<0){
            cout<<"NO\n";
            return;
        }
    }

    for(int i=2;i<=n;i++){
        if(a[i]!=a[i-1]){
            cout<<"NO\n";
            return;
        }
    }

    if(a[1]%(1+n)!=0){
        cout<<"NO\n";
            return;
    }

    cout<<"YES\n";

}
    

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;
    while(ct--){
        solve();
    }

    return 0; 
}

E. Lost Soul

先考虑没有删除操作的情况

假设有一个数x

在a,b数组中有三种情况:

x , x
_ , _


x , _
x , _

x , _ , _
x , _ , x (x的上下位置可以互换)

假设第一个x出现的位置是i,则这三种情况都可以让,前i个位置匹配上

现在考虑对第1,3中情况,删除一些东西,

x , _ , _ , _ , x
_ , _ , _ , _ , _ ,

可以把中间三列全删掉

x , _ , _ , _ , _
x , _ , _ , _ , x
可以把中间三列全删掉

假设第一个x出现的位置是i,则这三种情况,都可以删除一些数,让前i个位置匹配上

代码实现可以从后往前,用map分别维护a和b数组

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n;
    cin>>n;

    vector<int> a(n+1),b(n+1);
    int ans=0;

    for(int i=1;i<=n;i++){
        cin>>a[i];
    }

    for(int i=1;i<=n;i++){
        cin>>b[i];
    }

    map<int,int> mpa,mpb;

    for(int i=n;i>=1;i--){
        if(mpa[a[i]]) ans=max(ans,i);
        if(mpb[b[i]]) ans=max(ans,i);

        if(a[i]==b[i]) ans=max(ans,i);
        if(i>1 && (mpb[a[i-1]] || mpa[b[i-1]])) ans=max(ans,i-1);
        mpa[a[i]]++;
        mpb[b[i]]++;
    }
    cout<<ans<<endl;
}
    

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;
    while(ct--){
        solve();
    }

    return 0; 
}

F. Wildflower

因为只有1,2两个值,则最多有两个叶节点

如果叶节点只有一个,则树是一条链,答案是2^n

如果叶节点有两个,分别是a,b,u=lca(a,b)

则 u 和 u 以上的点,可以随便选

对于a和b,假设a是深度更深的

设b到u中间有t个点,(算上b,不算u)

如果a等于1,则b一定等于2,此时从b开始,t个点一定是确定的,从a开始,t+1个点一定是确定的

如果a等于2,则b一定等于1,此时从b开始,t个点一定是确定的,从a开始,t个点一定是确定的

不确定的点,选1选2都可

在纸上画一个,有两个叶子的树,手推一下就能看出来

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 1e9+7;

void solve(){
    int n;
    cin>>n;
    vector<vector<int>> g(n+10);

    vector<int>fa(n+10),dep(n+10),sz(n+10),son(n+10),top(n+10);
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    auto dfs1=[&](auto dfs1,int u,int pre)->void {
        fa[u]=pre;
        dep[u]=dep[pre]+1;
        sz[u]=1;

        for(auto v:g[u]){
            if(v==pre) continue;
            dfs1(dfs1,v,u);
            sz[u]+=sz[v];
            if(sz[v]>sz[son[u]]) son[u]=v;
        }
    };

    auto dfs2=[&](auto dfs2,int u,int tp)->void {
        top[u]=tp;
        if(!son[u]) return;
        dfs2(dfs2,son[u],tp);

        for(auto v:g[u]){
            if(v==son[u] || v==fa[u]) continue;
            dfs2(dfs2,v,v);
        }
    };

    auto lca=[&](int a,int b)->int {
        while(top[a]!=top[b]){
            if(dep[top[a]]<dep[top[b]]) swap(a,b);
            a=fa[top[a]];
        }
        if(dep[a]>dep[b]) return b;
        else return a;
    };

    dfs1(dfs1,1,0);
    dfs2(dfs2,1,1);

    int leaf=0,a=0,b=0;
    for(int i=2;i<=n;i++){
        if(g[i].size()==1){
            leaf++;
            if(a==0) a=i;
            else b=i;
        }
    }
    int ans=1;

    if(leaf>2){
        cout<<0<<endl;
        return;
    }

    if(leaf==1){
        for(int i=1;i<=n;i++){
            ans*=2;
            ans=ans%mod;
        }
        cout<<ans<<endl;
        return;
    }

    int u=lca(a,b);
    for(int i=1;i<=dep[u];i++){
        ans*=2;
        ans%=mod;
    }
    

    a=dep[a],b=dep[b];

    if(a==b){
        cout<<ans*2%mod<<endl;
        return;
    }

    if(a<b) swap(a,b);//a是较深的点
    int diff=a-b-1;

    ans*=3;
    for(int i=1;i<=diff;i++){
        ans=ans*2%mod;
    }
    cout<<ans<<endl;

}
    

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;
    while(ct--){
        solve();
    }

    return 0; 
}

G. Omg Graph

两种做法,最短路和并查集,只写了最短路,并查集之后补

稍微改变题目中最短路的定义,定义1到n的距离是:从1到n路径上的边权最大值。

要找到从1到n的最短路径,即让从1到n路径上的边权最大值最小

dijkstra找到,dist1:从1到每个点的最短路径, dist2:和从n到每个点的最短路径

再枚举每一条边{u,v,w},w作为答案中的min值,max值即为,max{w,dist1[u],dist2[v]}

枚举 (min+max)的最小值,即为答案

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
//using i128 = __int128_t;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n,m;
    cin>>n>>m;

    vector<vector<pii>> g(n+10);

    while(m--){
        int a,b,w;
        cin>>a>>b>>w;
        g[a].push_back({b,w});
        g[b].push_back({a,w});
    }

    auto dij=[&](int rt) {
        vector<int> dist(n+10,inf),st(n+10); 
        dist[rt]=0;
        priority_queue<pii,vector<pii>,greater<pii>> q;

        q.push({0,rt});
        
        while(q.size()){
            auto [w,u]=q.top();
            q.pop();
            if(st[u]) continue;
            st[u]=1;

            for(auto [v,d]:g[u]){
                if(max(d,w)<dist[v]){
                    dist[v]=max(d,w);
                    q.push({max(d,w),v});
                }
            }
        }
        return dist;
    };

    auto dist1=dij(1),dist2=dij(n);

    int ans=inf;
    for(int u=1;u<=n;u++){
        for(auto [v,w]:g[u]){
            ans=min(ans,max({dist1[u],dist2[v],w})+w);
        }
    }

    cout<<ans<<endl;
}
    

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;
    while(ct--){
        solve();
    }

    return 0; 
}
posted @ 2025-06-09 05:15  LYET  阅读(642)  评论(0)    收藏  举报