Codeforces Round # 697 (Div. 3) 문제 해결 보고서

제목 링크 : 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;
}

 

추천

출처blog.csdn.net/weixin_43911947/article/details/113175278