Codeforces Round #697 (Div. 3)
C. Ball in Berland
思路: 思维
该题的题意是在所有的男女舞伴中选取两对,其中男舞伴和女舞伴均只能出现在一对中,问这样的取法总共有多少种。
首先所有的对数不可能重复,即同一对不可能出现两次。因此我们可以记录所有对数中每个人出现的次数。
然后遍历每一对,对于每一对的男女舞伴,只需在总对数中减去 男舞伴出现的次数 和 女舞伴出现的次数再加1(男女舞伴多减了一次),就是能和这对舞伴匹配的所有方案数,最后的答案还是除以2,因为重复计算。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define maxn 200005
using namespace std;
struct Pair
{
int Boy;
int Girl;
}a[maxn];
int boy[maxn];
int girl[maxn];
void solve()
{
int A, B ,k;
cin >> A >> B >> k;
for(int i = 0; i <= max(A, B); i++) boy[i] = girl[i] = 0;
for(int i = 1; i <= k; i++)
{
cin >> a[i].Boy;
boy[a[i].Boy]++;
}
for(int i = 1; i <= k; i++)
{
cin >> a[i].Girl;
girl[a[i].Girl]++;
}
long long sum = 0;
for(int i = 1; i <= k; i++)
{
sum += ( k - (boy[a[i].Boy] + girl[a[i].Girl] - 1));
}
cout << sum/2 << endl;
}
int main()
{
int t = 1;
scanf("%d", &t);
while(t--)
solve();
return 0;
}
D. Cleaning the Phone
思路:二分
这题的题意是有一堆的app,每个app有一个占用的内存和重要程度,现在要求清理若干个app,使得清理的内存至少有M,且重要程度的总和最小。
刚开始的思路是贪心,但贪心其实在处理的过程中很多细节不能直接处理,需要打补丁。
观察重要程度可以发现总共只有两种重要程度,因此马上可以想到一个O(nlogn)的做法:将两种重要程度用两个数组来统计,然后每次数组从小到大排序后,再求他们的前缀和,接下来我们可以遍历重要程度的为1的数组,每次取完重要程度为1的app后,因为前缀和已经计算,可以马上求出当前已经清理的内存cur, 那么接下里只用在重要程度为2的app中直接二分查找到至少为 m-cur 的内存的左边边界即可。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define maxn 200005
using namespace std;
long long a[maxn];
int binary_Search(vector<long long> &a, long long k)
{
int left = 0;
int right = a.size();
while(left < right)
{
int mid = (left + right) / 2;
if(a[mid] >= k) right = mid;
else if(a[mid] < k) left = mid+1;
}
return left;
}
void solve()
{
long long n, m;
scanf("%lld%lld", &n ,&m);
vector<long long> a1, a2;
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
for(int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
if(x == 1) a1.push_back(a[i]);
else a2.push_back(a[i]);
}
sort(a1.rbegin(), a1.rend());
sort(a2.rbegin(), a2.rend());
for(int i = 1; i < a1.size(); i++) a1[i] = a1[i] + a1[i-1];
for(int i = 1; i < a2.size(); i++) a2[i] = a2[i] + a2[i-1];
long long nowai = MAXN;
long long nowbi = 0;
long long index = binary_Search(a1, m);
if(index != a1.size()) nowai = min(nowai, (index + 1));
for(long long i = 0; i < a2.size(); i++)
{
if(a2[i] >= m)
{
nowai = min(nowai, (i+1)*2);
break;
}
int index = binary_Search(a1, m-a2[i]);
//cout << "index = " << index << endl;
if(index != a1.size()) nowai = min(nowai, (i+1)*2 + (index + 1));
//cout << " nowai = " << nowai << endl;
}
if(nowai == MAXN) cout << "-1" << endl;
else cout << nowai << endl;
}
int main()
{
int t = 1;
scanf("%d", &t);
while(t--)
solve();
return 0;
}
E. Advertising Agency
思路:杨辉三角
这题的题意是有n个博主,每个博主i有ai个粉丝,现在要求挑选k个博主,使得挑选的博主的粉丝数总和最多。
显然我们需要记录相同粉丝数的博主个数,然后从大到小挑选博主。假设当前最多粉丝数的博主有cnt个,如果cnt > k, 显然需要有从cnt中挑选k个博主的所有方案;如果cnt <= k, 则只有一种方案数,即全选,然后 k = k - cnt。
这里从cnt中挑选k个博主的所有方案显然是个组合数,可以用杨辉三角来计算。
杨辉三角的递推式为 mat[i][j] = (mat[i-1][j-1] + mat[i-1][j]) ,需要注意每次都要取余。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define maxn 1005
#define mod 1000000007
using namespace std;
long long mat[maxn][maxn];
long long combinat(int m, int n) {
int i, j;
if(n == 0 || m == n)
return 1;
for(j = 0; j <= n; j++)
{ // 只要计算n列就行了,不用计算后面的
mat[j][j] = 1;
for(i = j+1; i <= m; i++)
{
if(j == 0)
mat[i][j] = 1;
else
mat[i][j] = (mat[i-1][j-1]%mod + mat[i-1][j]%mod) % mod;
} // 计算Cmn
}
return mat[m][n];
}
int cnt[maxn];
void solve()
{
int n, k;
cin >> n >> k;
for(int i = 1; i < maxn; i++) cnt[i] = 0;
for(int i = 1; i <= n; i++)
{
int x;
cin >> x;
cnt[x]++;
}
long long ans = 1;
int index = 1000;
while(1)
{
while(cnt[index] == 0) index--;
if(index <= 0 || k == 0) break;
if(cnt[index] > k)
{
ans = (ans%mod * combinat(cnt[index], k)%mod) % mod;
k = 0;
}
else k -= cnt[index];
index--;
}
cout << ans << endl;
}
int main()
{
int t = 1;
scanf("%d", &t);
while(t--)
solve();
return 0;
}
F. Unusual Matrix
思路: 思维
这题的题意是给定一个矩阵,每次只能对矩阵的某一行或这某一列进行异或操作,然后问能不能在经过一些操作之后将该矩阵变成目标矩阵。
首先得发现一些特点。
(1) 异或操作就是将1变成0, 将0变成1。
(2) 对于某一行或者某一列的异或操作,实质上只能操作一次,操作两次的时候就是变回初始的矩阵,操作三次的时候就是操作一次的矩阵。
(3) 矩阵的行列操作没有先后的顺序关系。
因此我们考虑矩阵的第一行,遍历每个元素,如果该元素与目标矩阵不同,那么显然要操作一次,这里进行一次列操作,最后遍历完之后,第一行的元素一定和目标矩阵的第一行完全相同。
接下来考虑第二行, 遍历每个元素,如果该元素与目标矩阵不同,那么显然要操作一次,由于第一行的所有元素已经完全相同了,所以每行如果存在与目标矩阵不同的元素时,只能进行行操作了。
按这样的方式检查每一行即可。
需要注意的是,这里还缺少一种情况,因为第一行的元素与目标矩阵正好完全不同的时候,可以事先将第一行进行异或操作一次。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define maxn 1005
using namespace std;
int n;
bool check(vector<vector<int> > a, vector<vector<int> > &b)
{
for(int i = 0; i < n; i++)
{
if(a[0][i] != b[0][i])
{
for(int j = 0; j < n; j++) a[j][i] ^= 1;
}
}
for(int i = 0; i < n; i++)
{
int Xor = (a[i][0] ^ b[i][0]);
for(int j = 1; j < n; j++)
{
if(Xor != (a[i][j] ^ b[i][j]))
return false;
}
}
return true;
}
void solve()
{
cin >> n;
string s;
vector<vector<int> > a(n), b(n);
for(int i = 0; i < n; i++)
{
cin >> s;
for(int j = 0; j < n; j++) a[i].push_back(s[j] - '0');
}
for(int i = 0; i < n; i++)
{
cin >> s;
for(int j = 0; j < n; j++) b[i].push_back(s[j] - '0');
}
for(int times = 1; times <= 2; times++)
{
if(check(a, b))
{
cout << "YES" << endl;
return;
}
for(int i = 0; i < n; i++)
{
a[0][i] ^= 1;
}
}
cout << "NO" << endl;
}
int main()
{
int t = 1;
cin >> t;
while(t--)
solve();
return 0;
}
G. Strange Beauty
思路:思维
这题的题意是: 一个序列是美丽的序列当且仅当对于序列的中的任意一个数ai, 都有序列的中的其他任何数aj(i != j), 使得 ai | aj 或者 aj | ai。现在给定一个序列,问至少去掉多少个数之后可以成为美丽的序列。
首先我们应该从小到大处理这个序列,这样可以只考虑ai | aj 或者 aj | ai 中的一种情况,即对于一个数ai, 那么它一定是 2 * ai, 3 * ai, … n * ai 的因数。
为此我们需要记录序列中每个数出现过的次数, 然后从小到大遍历这个序列的时候 , 对于每个数,用logn的时间去更新它的倍数所能包含的因数个数。
最后取包含最大因数个数的值,然后用n减去这个数即可。、
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define maxn 200005
using namespace std;
int cnt[maxn], res[maxn];
void solve()
{
int n;
cin >> n;
int maxv = 0;
for(int i = 1; i < maxn; i++) cnt[i] = res[i] = 0;
for(int i = 1; i <= n; i++)
{
int x;
cin >> x;
maxv = max(maxv, x);
cnt[x]++;
}
for(int i = 1; i <= maxv; i++)
{
if(!cnt[i]) continue;
res[i] += cnt[i];
for(int j = 2*i; j <= maxv; j += i)
res[j] = max(res[j], res[i]);
}
int ans = 0;
for(int i = 1; i <= maxv; i++)
ans = max(ans, res[i]);
cout << n - ans << endl;
}
int main()
{
int t = 1;
cin >> t;
while(t--)
solve();
return 0;
}