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; }
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; }
方法二:
我们从题目中可以得出 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; }
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:
#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; }
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; }
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; }
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; }
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; }
(D、F、G)补题