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)补题

 

posted @ 2021-01-27 21:02  Choose_and_be_chosen  阅读(157)  评论(0编辑  收藏  举报