Codeforces Round #485 (Div. 2) A~F 个人题解

Dashboard - Codeforces Round #485 (Div. 2) - Codeforces

A. Infinity Gauntlet

题意:给你一个大小为6的A到B的映射,现在给出一些A中的元素,问A中剩下的元素映射之后是什么?

思路:开个map记录一下,然后模拟一下过程就OK了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+5;
const ll mod =998244353;
map<string,string>mp;
map<string,bool>q;
void solve(){
    ll n;cin>>n;
    for(int i=1;i<=n;++i){
        string s;cin>>s;
        q[s]=true;
    }
    cout<<6-n<<'\n';//已经拿了n个,那一定剩下6-n个
    for(auto w:mp){
        if(q[w.first])continue;
        cout<<w.second<<'\n';
    }
}
int main() {
    mp["purple"]="Power";
    mp["green"]="Time";
    mp["blue"]="Space";
    mp["orange"]="Soul";
    mp["red"]="Reality";
    mp["yellow"]="Mind";
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;//cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

B. High School: Become Human

题意:问x^yy^x的大小关系?

思路:俩边同时取对数(任意)变成比较y\log_exx\log_e y的大小关系(精度意外的不错?)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+5;
const ll mod =998244353;
void solve(){
    int a,b;cin>>a>>b;
    double la=log(a),lb=log(b);//C++自带的以e为底的log函数
    if(b*la==a*lb)cout<<"=\n";
    else if(b*la<a*lb)cout<<"<\n";
    else cout<<">\n";
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;//cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

C. Three displays

题意:一个序列有n个数,找到其中三个数,他们的下标i,j,k满足i<j<k,并且s_i<s_j<s_k

问,这样的最小的c_i+c_j+c_k的和?

思路:发现是可以DP的,定义DP[i][j]为前i个数,以第i个数为结尾的长度为j的满足条件的最小答案,转移也很好转移,具体可以看代码。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+5;
const ll mod =998244353;
ll s[3005],c[3005];
ll dp[3005][4];
void solve(){
    int n;cin>>n;
    for(int i=1;i<=n;++i)cin>>s[i];
    for(int i=1;i<=n;++i)cin>>c[i];
    for(int i=0;i<=n;++i){
        for(int j=0;j<=3;++j)dp[i][j]=1e18;//初始要赋值最大,因为我们有取min操作
    }
    for(int i=1;i<=n;++i){
        dp[i][1]=c[i];//长度为1肯定是自己。
        for(int j=1;j<i;++j){
            if(s[i]>s[j]){
                dp[i][2]=min(dp[i][2],dp[j][1]+c[i]);//长度为2从前面长度为1的答案转移
                dp[i][3]=min(dp[i][3],dp[j][2]+c[i]);//长度为3从前面长度为2的答案转移
            }
        }
    }ll ans=1e18;
    for(int i=3;i<=n;++i)ans=min(ans,dp[i][3]);//找最优答案
    if(ans==1e18)cout<<-1<<'\n';
    else cout<<ans<<'\n';
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;//cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

D. Fair

题意:一个n个点,m条边的图(边权为 1 ),保证图是联通的,每个点都有一个类型为a_i的物品,任意俩个点之间的距离是他们之间最短路的距离,问对于i \in {1,2,3,.......n}每一个点来说,找到s个不同的点,使得他们之间的物品类型两两不同且他们到点i的距离最短

思路:发现k和s最大值不会超过100,也就是最多100个物品,那么,我们可以对与每一种类型来考虑,BFS求出每一种类型到所有点的最短距离,对于点 i ,答案就是100个类型中到i的距离最短的s个点,排个序就好了。

复杂度的话,BFS每个点只看k次,查询时有个大小为k的排序 O(nklogk)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+5;
const ll mod =998244353;
ll n,m,k,s;
ll a[N];
vector<ll>d[105],mp[N];
ll dp[N][105];
void solve(){
    cin>>n>>m>>k>>s;
    for(int i=1;i<=n;++i){
        cin>>a[i];
        d[a[i]].push_back(i);//每一个类型的下标
    }
    for(ll i=1,u,v;i<=m;++i){
        cin>>u>>v;
        mp[u].push_back(v);// 连个边
        mp[v].push_back(u);// 双向的
    }
    for(int i=1;i<=n;++i){
        for(int j=1;j<=k;++j)dp[i][j]=1e18;//初始化最大,有取min
    }
    //BFS
    for(int i=1;i<=k;++i){
        queue<ll>q;
        for(auto u:d[i]){
            q.push(u);
            dp[u][i]=0;
        }
        while(!q.empty()){
            ll g=q.front();q.pop();
            for(auto v:mp[g]){
                if(dp[v][i]<=dp[g][i]+1)continue;//如果我们要到的点已经搜过了就不用看了
                dp[v][i]=dp[g][i]+1;
                q.push(v);
            }
        }
    }
    for(int i=1;i<=n;++i){
        sort(dp[i]+1,dp[i]+k+1);//排个序,因为只要前s小
        ll ans=0;
        for(int j=1;j<=s;++j)ans+=dp[i][j];
        cout<<ans<<' ';
    }
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;//cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

E. Petr and Permutations

题意:有一个1~n的全排列,一种操作,操作是随机选择俩个数交换顺序。

有俩个人,第一个人操作3n次,第二个人操作7n+1次

现在给出你操作完后的排序,问你是谁操作的

思路:容易发现当给出n后,这俩个人的操作数一个奇数次,另一个是偶数次,再结合奇数次操作后逆序对的变化一定是奇数,偶数次操作后逆序对的变化一定是偶数,很容易找到联系。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6+5;
const ll mod =998244353;
ll tree[N],n,res;
void change(ll i,ll d){
    for(;i<=n;i+=(i&-i))tree[i]+=d;
}
ll ask(ll i){
    ll ans=0;
    for(;i>=1;i-=(i&-i))ans+=tree[i];
    return ans;
}
//上面这些是用来求逆序对的数量的
void solve(){
    cin>>n;
    // 当n是偶数,3n是偶数,7n+1是奇数
    // 当n是奇数,3n是奇数,7n+1是偶数
    for(int i=1;i<=n;++i){
        ll x;cin>>x;
        change(x,1);
        res+=ask(n)-ask(x);
    }
    if((n+res)&1)cout<<"Um_nik\n";//如果是后者 那这俩个数相加一定是个奇数
    else cout<<"Petr\n"; //反之,一定是个偶数
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;//cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

F. AND Graph

题意:给你m个数,数的范围是0~2^n-1,如果其中俩个数x,y满足x\&y==0则连一条边,问最后有多少个联通块?

思路:对于每一位进行考虑,如果一个数x可以和另一个数y连边,那么另一个数y一定在是x为1的位上为0,其他任意,那我们只要DFS每一个点连接这谁就可以了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 5e6+5;
const ll mod =998244353;
ll n,m,x,w;
bool has[N],vis[N];
void dfs(ll x){
    if(vis[x])return ;
    vis[x]=true;
    for(int i=0;i<n;++i){
        int d=(1<<i);
        if((x&d)){
            dfs((x^d));
        }
    }
    if(has[x])dfs((x^w));//如果这个点是存在的证明他还是可以搜
}
void solve(){
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        cin>>x;has[x]=true;
    }
    ll limit=1ll<<n,ans=0;
    w=limit-1;
    for(int i=0;i<limit;++i){
        if(has[i]){
            if(vis[i])continue;
            vis[i]=true;//i 和 (i^w)先连一条边
            dfs((i^w));
            ans++;//搜了几次就证明有几个联通块了
        }
    }
    cout<<ans<<'\n';
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;//cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

本人还很菜,其中可能有些错误或者不严谨的地方,请见谅。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值