제목 링크 : https://codeforces.com/contest/1475
충격! 2021 년 1 월 25 일 저녁 23시 3 만 명이 넘는 사람들이 공동으로 코드 포스에 모였습니다! 서버가 멈춰서 결국 게임 전체가 득점되지 않습니다 (⊙﹏⊙)
A. 홀수 제수
이야기
T 데이터 그룹, 각 그룹은 정수 n을 제공하고, n에 1이 아닌 홀수 인자가 있는지 묻고 YES를 출력하고 그렇지 않으면 NO를 출력합니다.
범위 : (1≤t≤1e4, 2≤n≤10 ^ 14)
아이디어
비트 연산. 그는 짝수 인자를 보지 않기 때문에 항상 2로 나눈 다음 최종 n이 1인지, 1이면 모든 인자가 짝수인지, 그렇지 않으면 홀수 인자가 있는지 확인할 수 있습니다.
바이너리에서 1의 수를 직접 볼 수도 있습니다.
저 비트 연산을 직접 수행하고 맨 오른쪽 1의 위치를 취하고 마지막 이진수로 표시된 숫자로 시작한 다음 크기를 원본 자체와 직접 비교하여이 1의 위치가 앞인지 알 수 있습니다. 그것은 정면이고, 그 자체와 동일합니다. 즉, 그는 이상한 요인이 없으며 그 반대의 경우도 마찬가지입니다.
ac 코드
2 버전을 제외한 폭력
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
int t; scanf("%d", &t);
while(t --){
ll n; scanf("%lld", &n);
while(n % 2 == 0) n >>= 1;
if(n == 1) puts("NO");
else puts("YES");
}
return 0;
}
저 비트 버전
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit(x) x & (-x)
int main(){
int t; scanf("%d", &t);
while(t --){
ll n; scanf("%lld",&n);
ll d = lowbit(n);
if(d == n) puts("NO");
else puts("YES");
}
return 0;
}
B. 새해 번호
이야기
T 데이터 그룹, 각 그룹에는 정수 n이 주어집니다. n이 2020과 b 2021을 더한 결과가 될 수 있는지 물어보십시오.
범위 : (1≤t≤1e4, 1≤n≤10 ^ 6)
아이디어
n = a * 2020 + b * 2021은 연상 법칙에 의해 n = 2020 * (a + b) + b로 변환 될 수 있으며, 여기서 a + b는 n / 2020이되고 b는 n % 2020이됩니다. 이 방정식의 변환 조건은 b> = a입니다. b는 (a + b) * 2020에 병합하기에 충분하기 때문입니다.
ac 코드
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
int t; scanf("%d", &t);
while(t --){
int n; scanf("%d", &n);
int a1 = n % 2020;
int a2 = n / 2020;
if(a1 <= a2) puts("YES");
else puts("NO");
}
return 0;
}
C. Berland의 공
이야기
댄스에 남자와 여자가 참여하고 있고, k 페어가있어 두 사람이 함께 춤을 출 수있다. 그럼 네 사람이 2 개의 댄스 팀을 구성하는데 몇 가지 방법이 있나요?
데이터 t 그룹에서 각 그룹의 첫 번째 행에 세 개의 숫자 a, b, k가 있고 두 번째 행의 k 번호는 사람의 일련 번호 ai를 나타내고 세 번째 행 bi는 일련 번호를 나타냅니다. 그리고 아이와 비는 함께 춤을 출 수 있습니다.
범위 : (1≤t≤1e4, 1≤a, b, k≤2e5, 1≤ai≤a, 1≤bi≤b)
아이디어
두 개의 벡터 배열을 열고 저장합니다 .v1 [i]는 남자 i와 춤을 출 수있는 여성의 수를 나타내고 v2 [i]는 여자 i와 춤을 출 수있는 남자의 수를 나타냅니다.
그런 다음 v1 [i]를 횡단하면 나는 춤추는 파트너이고 v1 [i] .size () + v2 [it] .size ()-1은이 둘과 관련된 모든 사람들입니다. 그러면이 가장자리를 더 이상 가져올 수 없습니다. , 왜냐하면 그것이 취해지면 그것은 점 i 또는 그것을 지적하는 약간 반복적 일 것입니다. 나머지는 k- (v1 [i] .size () + v2 [it] .size ()-1)이고,이 관계는 i와 그것과 반복되지 않으며, 두 번째 댄스 팀으로 사용될 수 있습니다. 순회 누적은 그러나 첫 번째 쌍과 두 번째 쌍에 대한 순서가 없으므로 최종 결과를 2로 나누는 것을 잊지 마십시오.
ac 코드
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 2e5 + 5;
vector<int> v1[maxn], v2[maxn];
int a[maxn], b[maxn];
int main(){
int t; scanf("%d", &t);
while(t --){
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= n; i ++) v1[i].clear();
for(int i = 1; i <= m; i ++) v2[i].clear();
for(int i = 1; i <= k; i ++){
scanf("%d", &a[i]);
}
for(int i = 1; i <= k; i ++){
scanf("%d", &b[i]);
v1[a[i]].push_back(b[i]);
v2[b[i]].push_back(a[i]);
}
ll ans = 0;
for(int i = 1; i <= n; i ++){
for(auto it : v1[i]){ //遍历vector v1[i]
ans += k - (v1[i].size() + v2[it].size() - 1);
}
}
cout << ans / 2 << endl;
}
return 0;
}
D. 전화기 청소
이야기
메모리 지우기, 각 응용 프로그램에는 AI 메모리와 BI 값이 있습니다.
t 데이터 그룹, 두 정수의 각 그룹 n, m, 각각 n 개의 응용 프로그램과 정리해야하는 메모리를 나타냄 s, 다음 줄은 응용 프로그램 메모리 ai를 나타내는 n 개의 숫자이고, 다음 줄은 응용 프로그램 값을 나타내는 n 개의 숫자입니다. bi, 정리 된 메모리가 m 이상이라는 전제하에 삭제 값이 최소화되고 최소값이 출력되는 곳을 물어보십시오.
범위 : (1≤t≤1e4,1≤n≤2e5,1≤m≤1e9,1≤ai≤1e9,1≤bi≤2)
아이디어
먼저 두 개의 배열을 열어 값이 각각 1, 2 인 모든 애플리케이션을 저장 한 다음 모든 애플리케이션의 메모리 크기에 따라 대형에서 소형으로 정렬합니다. 최종 선택은 값 1과 b 값 2 여야합니다. a를 선택하려면 메모리의 가장 큰 합계가 있어야합니다. 정렬 된 배열에 대한 접두사 합계를 찾은 다음 접두사 및 suma [i]에 대해 값이 1 인 i 개의 가장 큰 메모리를 사용하는 것을 의미합니다. 그런 다음 s-suma [i]가 남아 있으면 값 2에서 가져와 여기에서 동일한 작업을 수행하고 접두사와 합산을 찾은 다음 s-suma [i]보다 크거나 같은 첫 번째 첨자를 찾습니다. 초과 범위는 존재하지 않음을 나타냅니다 .ID가 있으면 s 이상인 메모리를 삭제하고 그 값의 합이 i + id * 2이면 최소값 만 업데이트하면됩니다.
ac 코드
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 2e5 + 5;
const int inf = 0x3f3f3f3f;
int w[maxn], v[maxn];
int main(){
int t; cin >> t;
while(t --){
vector<ll> v1, v2; //v1存价值为1的应用的内存,v2存价值为2的应用的内存
int n, s;
ll sum = 0;
cin >> n >> s;
for(int i = 1; i <= n; i ++){
cin >> w[i];
sum += w[i];
}
for(int i = 1; i <= n; i ++){
cin >> v[i];
if(v[i] == 1) v1.push_back(w[i]);
else v2.push_back(w[i]);
}
if(sum < s){ //如果所有应用内存加起来都低于s,那直接出-1
puts("-1");
continue;
}
sort(v1.begin(), v1.end(), greater<int>()); //按从大到小排
sort(v2.begin(), v2.end(), greater<int>());
v1.insert(v1.begin(), 0); //选0个应用的时候,内存是0
v2.insert(v2.begin(), 0);
for(int i = 1; i < v1.size(); i ++) v1[i] += v1[i - 1]; //求前缀
for(int i = 1; i < v2.size(); i ++) v2[i] += v2[i - 1];
int ans = inf;
for(int i = 0; i < v1.size(); i ++){
int id = lower_bound(v2.begin(), v2.end(), s - v1[i]) - v2.begin(); //找剩余内存的位置
if(id < v2.size()) ans = min(ans, i + id * 2); //更新最小值
}
cout << ans << endl;
}
return 0;
}
E. 광고 대행사
이야기
T 데이터 그룹, 각 데이터 그룹은 두 개의 숫자 n, k, n 개의 숫자 ai를 제공하고 최대 값을 얻기 위해 k 개의 숫자를 선택하도록 요청합니다. 선택하는 방법에는 여러 가지가 있습니다 (mod 1e9 + 7).
범위 (1≤t≤1000, 1≤k≤n≤1000)
아이디어
조합 수. 최대 값은 큰 것에서 작은 것까지 구해야하며 그 수는지도로 유지하며 ai를 구하면 d ai가 있고 m은 취하기 쉽다면 C (d, m) 방법이 있습니다.
n의 범위는 크지 않으며 조합 수 배열 c는 Yang Hui의 삼각형에 따라 초기화 될 수 있습니다. c [i] [j] = c [i-1] [j] + c [i-1] [j-1].
ac 코드
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
int c[1005][1005];
void init(){
c[0][0] = 1;
for(int i = 1; i <= 1000; i ++){
c[i][0] = 1;
for(int j = 1; j <= i; j ++) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
int main(){
init();
int t; scanf("%d", &t);
while(t --){
map<int, int, greater<int> >mp; //按照第一键值从大到小排
int n, m;
scanf("%d%d", &n, &m);
while(n --){
int x; scanf("%d", &x);
mp[x] ++;
}
for(auto it : mp){ //从大到小取
int d = it.second;
if(d >= m) { // m不够,才有取法
printf("%d\n", c[d][m]);
break;
}
m -= d; // m足够,那么取完d
}
}
return 0;
}
F. 특이한 매트릭스
이야기
t 개의 데이터 그룹이 있고 각 데이터 그룹에는 n이 있고 그 다음에는 n * n 행렬 A와 B가 2 개 있습니다. 값은 0 또는 1입니다. 뒤집을 행 (0에서 1, 1에서 0)을 선택하고 뒤집을 열을 선택하는 두 가지 작업이 있습니다. 이 두 가지 작업을 통해 행렬 A를 행렬 B로 바꿀 수 있는지 물어보십시오.
범위 : (1≤t≤1000, 1≤n≤1000)
아이디어
구조 질문. 두 종류의 반전, 순서는 결과에 영향을 미치지 않습니다. 먼저 A [i] [j]와 B [i] [j]가 같은지 여부를 나타내는 2 차원 행렬 C를 정의합니다. 같지 않으면 1이고 같으면 문제는 제로 행렬로 변환됩니다. 그런 다음 각 행의 첫 번째 숫자가 1인지 확인하고 1이면 행 변환을 수행합니다.이 경우 행 변환이 종료되고 각 행이 동일한 지 확인합니다. 모두 동일하면 열 변환으로 제거 할 수 있으며 모두 다릅니다.
ac 코드
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1005;
string a[maxn], b[maxn], c[maxn];
int main(){
int t; cin >> t;
while(t --){
int n; cin >> n;
for(int i = 0; i < n; i ++) cin >> a[i];
for(int i = 0; i < n; i ++) cin >> b[i];
for(int i = 0; i < n; i ++) {
for(int j = 0; j < n; j ++) {
if(a[i][j] == b[i][j]) c[i] += "0";
else c[i] += "1";
}
if(c[i][0] == '1'){
for(int j = 0; j < n; j ++){ //行变换,1-1=0,1-0=1 实现翻转
c[i][j] = '1' - (c[i][j] - '0');
}
}
}
int flag = 1;
for(int i = 0; i < n; i ++){
if(c[i] != c[0]){ //判断每行是否相同
flag = 0;
break;
}
}
if(flag) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
G. 이상한 아름다움
이야기
완벽한 시퀀스 : 시퀀스에서 a와 b의 두 값을 취하고 a를 b로 나눌 수 있거나 b를 a로 나눌 수 있습니다.
그런 다음 길이 n의 시퀀스를 제공하고 나머지 시퀀스를 완벽한 시퀀스로 만들기 위해 최소한 몇 개의 숫자를 삭제하도록 요청하십시오.
범위 : (1≤t≤10, 1≤n≤2e5, 1≤ai≤2e5)
아이디어
dp. 질문의 의미는 재귀 형식의 체와 유사한 가장 긴 완벽한 시퀀스를 찾는 것으로 변환 될 수 있습니다.
여기서 완벽한 시퀀스는 오름차순이라고 가정합니다. dp [i]는 끝이 i 인 완벽한 시퀀스의 최대 길이를 나타내고 cnt [i]는 i의 수를 나타내고 다음 값 j는 i 와만 관련이 있기 때문에 i의 배수 여야합니다. 그런 다음 순회를 두 배로 늘립니다.
재귀 적으로 값을 업데이트 한 다음 i가 끝이 될 수 있으면 j도 끝이 될 수 있으므로 dp [j]는 dp [i]와 같을 수 있고 dp [j]는 가장 큰 dp [i]를 가져옵니다. 즉, dp [j] = max (dp [i]) (j % i == 0)입니다. 마지막으로 dp [i]의 최대 값을 업데이트합니다.
최종 답은 n-maximum입니다. 원하는 것은 삭제 된 숫자이기 때문입니다.
ac 코드
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 2e5 + 5;
int dp[maxn], cnt[maxn];
int main(){
int t; scanf("%d", &t);
while(t --){
int n, mx = -1; scanf("%d", &n);
for(int i = 1; i <= n; i ++){
int x; scanf("%d", &x);
cnt[x] ++;
mx = max(mx, x);
}
int ans = 0;
for(int i = 1; i <= mx; i ++){
dp[i] += cnt[i]; //加上i的个数,因为后面递推的时候没有算上他的个数
for(int j = i * 2; j <= mx; j += i){
dp[j] = max(dp[j], dp[i]);
}
ans = max(ans, dp[i]);
}
printf("%d\n", n - ans);
for(int i = 1; i <= mx; i ++){
cnt[i] = dp[i] = 0;
}
}
return 0;
}