zoukankan      html  css  js  c++  java
  • Codeforces Round #697 (Div. 3) 题解

    Codeforces Round #697 (Div. 3)

    大晚上的,好不容易打个比赛,结果第二天兴致勃勃看分数,发现unrated了,吐血!!!!!!!!!!!

    A. Odd Divisor

    Problem:

      给你一个 n ,问是否有大于 1 的奇数因子

    Solution:

      出了2的次方,其它的都会有奇数因子(自己随便验证一下就好了

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e5 + 10;
    map<ll,int> mp;
    void init(){
        rep(i,1,55){
            mp[(1ll << i)] ++;
        }
    }
    int main(){
        IOS;
        init();
        int t;
        cin>>t;
        ll n;
        while(t --){
            cin>>n;
            if(mp[n]){
                cout<<"NO"<<endl;
            }else{
                cout<<"YES"<<endl;
            }
        }
        return 0;
    }
    View Code

    B. New Year's Number

    Problem:

      给你一个 n ,问是否能够拆分成 2020 和 2021 的和

    Solution:

      方法一:

        数据比较小,n的最大值才 1e6 ,而满足 2020 * x >= 1e6 的 x 最小值为 1e6/2020 ,所以我们只需要枚举出所有的 2020 * x + 2021 * y就好了

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e5 + 10;
    map<int,int> mp;
    void init(){
        for(int i = 0;i <= 500;i ++){
            for(int j = 0;j <= 500;j ++){
                mp[i*2020 + j*2021] = 1;
            }
        }    
    }
    int main(){
        IOS;
        init();
        int t;
        cin>>t;
        ll n;
        while(t--){
            cin>>n;
            if(mp[n] == 1){
                cout<<"YES"<<endl;
            }else{
                cout<<"NO"<<endl;
            }
        }
        return 0;
    }
    View Code

      方法二:

        我们从题目中可以得出 2020*x + 2021*y = n --> 2020*x + (2020 + 1)*y = n --> 2020*(x + y) + y = n。

        由上面式子我们可以知道(x + y)是 n 中有几个 2020 ,y 是 n 中除去所有2020 剩下的值,即 (x + y) = n / 2020 ,y = n%2020,有了上面的两个式子,我们就可以求出 x 和 y 的值了,只要 x >= 0 && y >= 0即是 YES 反之是 NO

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e5 + 10;
    int main(){
        IOS;
        int t;
        cin>>t;
        ll n;
        while(t--){
            cin>>n;
            ll y = n % 2020,x = n/2020 - y;
            if(x >= 0){
                cout<<"YES"<<endl;
            }else{
                cout<<"NO"<<endl;
            }
        }
        return 0;
    }
    View Code

    C. Ball in Berland

    Problem:

      a 个男的,b 个女的,有 k 个男女组合(不会出现两个重复的组合)。从 k 个组合中选取 2 个组合,问有多少种选取方式,可以让选出的两个组合中的人不会同时出现在两个组合中。

      例如a=3,b=4,k=4,(1,2),(1,3),(2,2),(3,4)。我们可以选择(1,3)和(3,4),但是不能选择(1,2)和(2,2),因为2这个女的同时出现在两个组合中了。

    Solution:

      其实就是对于每个组合 (a,b),计算出能够有多少个组合中不会出现男 a 和 女 b ,对于k个组合,我们计算出男生 a 出现过几次,女生 b 出现过几次,

      然后用 k - 男生 a 出现过次数 -女生 b 出现过次数 + 1(由于不会出现两个重复的组合,所以男 a 和 女 b 在同一个组合的数量只会为 1,所以只会多减一次)。

      (最后答案要除以二)

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 2e5 + 10;
    struct Node{
        int a,b;
        Node(){}
        Node(int _a,int _b):a(_a),b(_b){}
    }nd[maxn];
    int ASum[maxn],BSum[maxn];
    int main(){
        IOS;
        int t,a,b,k;
        cin>>t;
        while(t--){
            cin>>a>>b>>k;
            rep(i,1,k){
                cin>>nd[i].a;
                ASum[nd[i].a] = 0;
            }
            rep(i,1,k){
                cin>>nd[i].b;
                BSum[nd[i].b] = 0;
            }
            rep(i,1,k){
                ASum[nd[i].a] ++;
                BSum[nd[i].b] ++;
            }
            ll ans = 0;
            rep(i,1,k){
                ll res = k - (ASum[nd[i].a] + BSum[nd[i].b] - 1);
                if(res > 0){
                    ans += res;    
                }
            }
            cout<<(ans)/2<<endl;
        }
        return 0;
    }
    View Code

    D. Cleaning the Phone

    Problem:

      有 n 个应用,每个应用占用 ai 大小的空间和由 bi 的方便点(bi 的值为1 或 2),问清除至少 m 大小的空间,最少会花费几个方便点。(即在 n 个应用中,如何删除一些任务,让空间大小大于等于m,并且方便点的和最小)

    Solution:

      由于 bi 为 1 或 2 ,所以我们可以在它上面进行操作。

      设选取 x 个 1 方便点的应用,y 个 2 方便点的应用,如何让x + y*2最小?

      我们能够知道,对于同样的方便点的应用,选取空间最大的一定是最好的,所以我们将 1 方便点和 2 方便点的应用进行按空间大小从小到大进行排序,然后我们可以假设我们选取了 y 个 2 方便点的应用,并且空间和为 sum,那么对于 1 方便点的应用我们就需要选取空间和大于等于 m - sum 空间大小,假设选取了x个,在这个过程中,我们不断的跟新x + y*2的最小值,最终就可以获得答案。

      由于如果我们直接用两个for循环遍历寻找答案的话,时间复杂度会达到O(n*n),所以我们必须进行优化,在寻找m - sum空间的时候,其实我们就是找第一个空间前缀和大于等于m - sum的下标,所以在这我们可以进行二分(直接用lower_bound)。注意,找不到m - sum空间大小的时候,就需要跳过。

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 2e5 + 10;
    ll t,n,m;
    int a[maxn],b[maxn];
    vector<ll> one,two;
    bool cmp(ll number1,ll number2){
        return number1 > number2;
    }
    int main(){
        IOS;
        cin>>t;
        while(t--){
            one.clear(),two.clear();
            cin>>n>>m;
            rep(i,1,n){
                cin>>a[i];
            }
            rep(i,1,n){
                cin>>b[i];
            }
            rep(i,1,n){
                if(b[i] == 1){
                    one.push_back(a[i]);
                }else{
                    two.push_back(a[i]);
                }
            }
            sort(one.begin(),one.end(),cmp);
            sort(two.begin(),two.end(),cmp);
            _for(i,1,two.size()){
                two[i] += two[i - 1];
            }
            ll ans = INF,sum = 0;
            int j = lower_bound(two.begin(),two.end(),m - sum) - two.begin() + 1;
            if(j <= two.size()) ans = min(ans,j * 2ll);
            _for(i,0,one.size()){
                sum += one[i];
                if(sum < m){
                    j = lower_bound(two.begin(),two.end(),m - sum) - two.begin() + 1;
                    if(j <= two.size()) ans = min(ans,j * 2ll + (i + 1));
                }else{
                    ans = min(ans,i + 1ll);
                }
            }
            if(ans == INF){
                cout<<-1<<endl;    
            }else{
                cout<<ans<<endl;
            }
        }
        return 0;
    }
    View Code

    E. Advertising Agency

    Problem:

      给n 个数的数组a,从中选取 k 个,当选取的 k 个数和为最大值时,问有多少种选法?最终答案对1e9 + 7取模。

      如:n = 4,k = 3,a = {1,3,1,2},则选取 3 个的最大值为 1 + 3 + 2 = 6,而选取方法由a[0] + a[1] + a[3]、a[1] + a[2] + a[3]两种

    Solution:

      找到选取 k 个数和最大时的最小值的数,然后计算 k 个数中需要这个最小值得数几个(假设为m)和这个最小值的数一共有几个(假设为n),最终的答案就是C(n,m),即从 n 个中选取 m 个有几种方法。

      其实就是考个排列组合取模,写上这个模板就好了。

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e3 + 10;
    int a[maxn];
    bool cmp(int v1,int v2){
        return v1 > v2;
    }
    const ll mod = 1e9+7;
    ll n,k,ans,inv[maxn],f[maxn];
    ll C(ll x,ll y){
        return f[x] * inv[y] % mod * inv[x - y] % mod;    
    }
    ll A(ll x,ll y){
        return f[x] * inv[x - y] % mod;
    }
    map<int,int> mp;
    int main(){
        IOS;
        inv[0] = f[0] = inv[1] = f[1] = 1;
        _for(i,2,maxn){
            inv[i] = ((mod - mod / i) * inv[mod % i]) % mod;
            f[i] = i;
        }
        _for(i,2,maxn){
            inv[i] = (inv[i] * inv[i - 1]) % mod;
            f[i] = (f[i] * f[i - 1])% mod;
        }
        int t,n,k;
        cin>>t;
        while(t --){
            mp.clear();
            cin>>n>>k;
            rep(i,1,n){
                cin>>a[i];
                mp[a[i]]  ++;
            }
            sort(a + 1,a + 1 + n,cmp);
            int num = 0,sum;
            rep(i,1,k){
                if(a[i] != a[i - 1]){
                    num = 0;
                    sum += a[i];
                }
                num ++;
            }
            cout<<C(mp[a[k]],num)<<endl;
        }
        return 0;
    }
    View Code

    F. Unusual Matrix

    Problem:

      给两个矩阵 a 和 b,其中 a 和 b 矩阵中只会出现 0 和 1,问经过以下操作后能否把 a 矩阵变成 b 矩阵。

      操作:

        (1)将某一行中的 0 变成 1,1 变成 0

        (2)将某一列中的 0 变成 1,1 变成 0

    Solution:

      首先,我们考虑若 a[ i ][ j ] != b[ i ][ j ],那么我们就必须在这个位置上进行一次操作(且只能进行一次操作,大于一次操作都是没有任何意义的),而如果我们对该位置的行进行操作的话,那么在这一行中其实在操作后还有不一样的话,那么只能够通过列进行操作了。由此我们可以知道,其实若 a 矩阵第 i 行和 b 矩阵第 i 行的第一个元素不相等,那么我们一定是需要进行操作的,若相等的话,其实我们这一行也没必要再去考虑需不需要进行操作了,因为这行往后遍历即使有不相等的,然后我们进行操作了,那也会让前面原本相等的变成不相等,这是没有任何意义的。对于列的操作也同理,我们只需要考虑每一列的第一个是否相等即可,不相等则进行操作。在行和列操作之后,我们就可以看 a 矩阵是否和 b 矩阵相等。

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e3 + 10;
    string a[maxn],b[maxn];
    int main(){
        IOS;
        int t,n;
        cin>>t;
        while(t --){
            cin>>n;
            rep(i,1,n){
                cin>>a[i];
            }
            rep(i,1,n){
                cin>>b[i];
            }
            rep(i,1,n){
                if(a[i][0] != b[i][0]){
                    for(int j = 0;j < n;j ++){
                        a[i][j] = (a[i][j] == '0'?'1':'0');
                    }
                }
            }
            rep(i,0,n){
                if(a[1][i] != b[1][i]){
                    for(int j = 1;j <= n;j ++){
                        a[j][i] = (a[j][i] == '0'?'1':'0');
                    }
                }
            }
            bool ans = true;
            rep(i,0,n){
                if(a[i] != b[i]){
                    ans = false;
                    break;
                }
            }
            cout<<(ans?"YES":"NO")<<endl;
        }
        return 0;
    }
    View Code

    G. Strange Beauty

    Problem:

      给定一个 n 个元素的数组 a,问最少删除 a 数组中的多少个元素可以使得在 a 数组中对于任意的两个元素 a[ i ] 和 a[ j ] (i != j)满足 a[ i ] 可以被 a[ j ] 整除,或者a[ j ] 可以被 a[ i ] 整除

    Solution:

      我们可以设 f[ i ] = x 为当 i 为序列最大的数时最多有x个因子,所以 f[ i ] = 值为  i  的数的个数  + max( f[ j ] ) j 是 i 的因子。

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 2e5 + 10;
    int a[maxn],cnt[maxn],f[maxn];
    int main(){
        IOS;
        int t,n;
        cin>>t;
        while(t --){
            memset(cnt,0,sizeof(cnt));
            memset(f,0,sizeof(f));
            cin>>n;
            rep(i,1,n){
                cin>>a[i];
                ++cnt[a[i]];
            }
            sort(a + 1,a + n + 1);
            int mxx = 0;
            _for(i,1,maxn){
                f[i] += cnt[i];
                for(int j = i + i;j < maxn;j += i){
                    f[j] = max(f[j],f[i]);
                }
                mxx = max(mxx,f[i]);
            }
            cout<<n - mxx<<endl;
        }
        return 0;
    }
    View Code

    (D、F、G)补题

  • 相关阅读:
    函数式宏定义与普通函数
    linux之sort用法
    HDU 4390 Number Sequence 容斥原理
    HDU 4407 Sum 容斥原理
    HDU 4059 The Boss on Mars 容斥原理
    UVA12653 Buses
    UVA 12651 Triangles
    UVA 10892
    HDU 4292 Food
    HDU 4288 Coder
  • 原文地址:https://www.cnblogs.com/liuzuolin/p/14336903.html
Copyright © 2011-2022 走看看