Codeforces Round #686 (Div. 3)

Codeforces Round #686 (Div. 3)

A. 给出一个错排。

题解:整体左移一位即可。

#include<bits/stdc++.h>

using namespace std;

int main(){
    int _;cin>>_;
    while(_--){
        int n;cin>>n;
        for(int i=1;i<n;++i)cout<<i+1<<" ";
        cout<<1<<endl;
    }

    return 0;
}

B. 找一个尽量小的出现一次的数字角标。

题解:记录一下角标,从小往大贪心。

#include<bits/stdc++.h>

using namespace std;

map<int,int>H;
map<int,int>idx;

int main(){
    int _;cin>>_;
    while(_--){
        H.clear();
        idx.clear();
        int n;cin>>n;
        for(int i=1;i<=n;++i){
            int x;cin>>x;
            ++H[x];
            idx[x]=i;
        }

        int flag=0;
        for(auto& p:H){
            if(p.second==1){
                cout<<idx[p.first]<<endl;
                flag=1;
                break;
            }
        }

        if(!flag){
            cout<<"-1"<<endl;
        }
    }

    return 0;
}

C. 给你n个数的数组,你需要选定一个数,这个数不能碰,选择包含其他数的段进行删除。问最少删除几次能使得整个数组只剩下这个数字?

题解:枚举数字,每次暴力往下一个跳就好了。

#include<bits/stdc++.h>

using namespace std;

const int N = 200005;

int n, a[N], vis[N], Next[N], last[N];

int main(){
    int _;cin>>_;
    while(_--){
        cin>>n;
        for(int i=1;i<=n+1;++i){
            vis[i]=Next[i]=last[i]=-1;
        }
        for(int i=1;i<=n;++i)cin>>a[i];
        for(int i=n;i>=1;--i){
            Next[i]=last[a[i]];
            last[a[i]]=i;
        }

        int ans=n;
        for(int i=1;i<=n;++i){
            if(vis[i]==-1){
                vis[i]=1;
                int t=(i==1)?0:1;
                int cur=i, nxt=Next[i];
                int flag=cur;
                while(nxt!=-1){
                    flag=max(flag,nxt);
                    vis[nxt]=1;
                    if(nxt!=cur+1)++t;
                    cur=nxt;
                    nxt=Next[nxt];
                }
                if(flag!=n)++t;
                ans=min(ans,t);
            }
        }

        cout<<ans<<endl;
    }

    return 0;
}

D. 给你一个数,你要拆成多个数之积,并且每一个数都是后面一个数的因子。

题解:质因数分解之后,最优构造取决于最大幂次。

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

const int N = 100005;

bool isNot[N];

int prime[N], tol;

void sieve(){
    for(int i=2;i<=100000;++i){
        if(!isNot[i]){
            prime[++tol]=i;
            for(int j=2*i;j<=100000;j+=i){
                isNot[j]=1;
            }
        }
    }
}

int main(){
    sieve();
    int _;cin>>_;
    while(_--){
        LL n;cin>>n;
        int mx=1;
        LL id=-1;

        for(int i=1;i<=tol;++i){
            if(1ll*prime[i]*prime[i]>n)break;
            int cnt=0;
            LL t=n;
            while(t%prime[i]==0){
                ++cnt;
                t/=prime[i];
            }
            if(cnt>mx){
                mx=cnt;
                id=prime[i];
            }
        }

        if(mx==1){
            cout<<"1"<<endl;
            cout<<n<<endl;
        }else{
            cout<<mx<<endl;
            for(int i=1;i<mx;++i){
                cout<<id<<" ";
                n/=id;
            }
            cout<<n<<endl;
        }
    }

    return 0;
}

E. 给你n个点的基环树,问你简单路径的条数。

题解:首先考虑只有环的情况,结果显然是n*(n-1)。考虑一颗树上,每一个点,到本树另一个点的简单路径只有一条。所以,我们要考虑所有树的尺寸就好了。关于这个,我们可以在无向图拓扑排序的时候,用一个DSU维护每棵树的尺寸。

#include<bits/stdc++.h>

using namespace std;

const int N = 200005;

typedef long long LL;

int n, deg[N], f[N], vis[N], sz[N];

vector<int> G[N];

inline int Find(int x){return x==f[x]?x:f[x]=Find(f[x]);}

int main(){
    int _;scanf("%d",&_);
    while(_--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            G[i].clear();
            deg[i]=0;
            f[i]=i;
            vis[i]=0;
            sz[i]=1;
        }
        for(int i=1;i<=n;++i){
            int u, v;
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
            ++deg[u];
            ++deg[v];
        }

        LL ans=1ll*n*(n-1);

        queue<int> q;
        for(int i=1;i<=n;++i){
            if(deg[i]==1){
                q.push(i);
                vis[i]=1;
            }
        }

        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(auto& v:G[u]){
                if(vis[v])continue;
                int fu=Find(u);
                int fv=Find(v);
                if(f[fu]!=f[fv]){
                    sz[fu]+=sz[fv];
                    f[fv]=fu;
                }
                if(--deg[v]==1){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }

        for(int i=1;i<=n;++i){
            if(i==f[i]&&sz[i]>1){
                ans-=1ll*sz[i]*(sz[i]-1)/2;
            }
        }

        cout<<ans<<endl;
    }
    return 0;
}

F. 给你n个数的数组,你现在要切这个数组为3段。第一段最大值等于第二段最小值,等于第三段最大值。

题解:(来自I-wen Huang)。我们考虑这个相等的值,并且从大到小开始考虑。如果最大值超过3个,那直接白给。否则我们从大到小开始考虑的时候,记录一个边界(这里指大于这个值的最左和最右的边界,原因在于,在这个边界里面最大值肯定不是我们当前考虑的这个值)。这个时候,我们需要数出在这个边界中的,当前考虑的这个值的个数,且我们需要记录这个边界里,大于当前值的个数。如果大于等于这个值的个数,小于区间长度,那肯定不合法(最小值不是当前数)。这个时候,一个前缀和一个后缀都是小于等于当前值的。我们只需要考虑,三个等号是不是都可以取到就可以了。

#include<bits/stdC++.h>

using namespace std;

const int N = 200005;

int a[N], n;

map<int, vector<int>> pos;

int main(){
    int _;scanf("%d",&_);
    while(_--){
        pos.clear();
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",a+i);
            pos[-a[i]].push_back(i);
        }

        int l=n+1, r=0;
        int flag=0;
        int gtr=0;
        for(auto& p:pos){
            int x=-p.first;
            vector<int> vec=p.second;
            if(l>n){
                int sz=(int)vec.size();
                if(sz>=3){
                    flag=1;
                    cout<<"YES"<<endl;
                    cout<<vec[1]-1<<" 1 "<<n-vec[1]<<endl;
                    break;
                }
            }else{
                int eql=0;
                for(auto& v:vec){
                    if(v>=l&&v<=r){
                        ++eql;
                    }
                }

                if(r-l+1<=(gtr+eql)){
                    if(eql>0){
                        if(vec[0]<l&&vec.back()>r){
                            flag=1;
                            cout<<"YES"<<endl;
                            cout<<l-1<<" "<<r-l+1<<" "<<n-r<<endl;
                            break;
                        }
                    }else{
                        if(l-1>1&&a[l-1]==x&&vec[0]<l-1&&vec.back()>r){
                            flag=1;
                            cout<<"YES"<<endl;
                            cout<<l-2<<" "<<r-l+2<<" "<<n-r<<endl;
                            break;
                        }

                        if(r+1<n&&a[r+1]==x&&vec[0]<l&&vec.back()>r+1){
                            flag=1;
                            cout<<"YES"<<endl;
                            cout<<l-1<<" "<<r-l+2<<" "<<n-r-1<<endl;
                            break;
                        }
                    }
                }
            }

            for(auto& v:vec){
                l=min(l,v);
                r=max(r,v);
            }

            gtr+=(int)vec.size();
        }

        if(!flag){
            cout<<"NO"<<endl;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/JohnRan/p/14041305.html