牛客周赛(Round 105)

A 小苯的xor构造


链接:https://ac.nowcoder.com/acm/contest/115861/A


反应过来:0 ^ X = X,这题就解决了


code

#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
#include<limits.h>
#define MOD 1000000007
#define mod 998244353
#define INF 0x3f3f3f3f
typedef long long ll;  
typedef unsigned long long ull;

// 自定义随机数生成器,避免与标准库冲突
ull xorshift64star(void) {
    static ull state = 234;
    state ^= state >> 12;
    state ^= state << 25;
    state ^= state >> 27;
    return state * 0x2545F4914F6CDD1DULL;
}
int max_(int p1, int p2) {
	return p1 > p2 ? p1 : p2;
}
int min_(int p1, int p2) {
	return p1 < p2 ? p1 : p2;
}
int cmp_up(const void* p1, const void* p2) {
	return *(int*)p1 - *(int*)p2;
}
int cmp_down(const void* p1, const void* p2) {
	return *(int*)p2 - *(int*)p1;
}
int gcd(int a, int b) {
	return a % b == 0 ? b : gcd(b, a % b);
}
int lowbit(int x) {
	return x & (-x); 
}
ll qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}


int main() {
    int k;
    scanf("%d", &k);
    printf("%d 0", k);
    return 0;
}

B 小苯的权值计算


链接:https://ac.nowcoder.com/acm/contest/115861/B


模拟过程就可以了,不过注意:特殊的,定义 gcd(0, x) = x,所以加个判断即可。


code

#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
#include<limits.h>
#define MOD 1000000007
#define mod 998244353
#define INF 0x3f3f3f3f
typedef long long ll;  
typedef unsigned long long ull;

// 自定义随机数生成器,避免与标准库冲突
ull xorshift64star(void) {
    static ull state = 234;
    state ^= state >> 12;
    state ^= state << 25;
    state ^= state >> 27;
    return state * 0x2545F4914F6CDD1DULL;
}
int max_(int p1, int p2) {
	return p1 > p2 ? p1 : p2;
}
int min_(int p1, int p2) {
	return p1 < p2 ? p1 : p2;
}
int cmp_up(const void* p1, const void* p2) {
	return *(int*)p1 - *(int*)p2;
}
int cmp_down(const void* p1, const void* p2) {
	return *(int*)p2 - *(int*)p1;
}
int gcd(int a, int b) {
	return a % b == 0 ? b : gcd(b, a % b);
}
int lowbit(int x) {
	return x & (-x); 
}
ll qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

int main() {
    int n;
    scanf("%d", &n);
    int a[20];
    for(int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    int x = a[0] ^ a[1];
    for(int i = 0; i < n - 1; i++) {
        a[i] = a[i] ^ a[i + 1];
        if(a[i]) x = gcd(x, a[i]);
    }
    printf("%d", x);
    return 0;
}

C 小苯的01矩阵构造


链接:https://ac.nowcoder.com/acm/contest/115861/C


首先明确 k 一定为偶数(改变一个数字其行列都变化,可以自己在纸上模拟),因为 k 在【0, 2*n],所以直接将对角线上 k / 2 的 0 改为 1 即可。


code

#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
#include<limits.h>
#define MOD 1000000007
#define mod 998244353
#define INF 0x3f3f3f3f
typedef long long ll;  
typedef unsigned long long ull;

// 自定义随机数生成器,避免与标准库冲突
ull xorshift64star(void) {
    static ull state = 234;
    state ^= state >> 12;
    state ^= state << 25;
    state ^= state >> 27;
    return state * 0x2545F4914F6CDD1DULL;
}
int max_(int p1, int p2) {
	return p1 > p2 ? p1 : p2;
}
int min_(int p1, int p2) {
	return p1 < p2 ? p1 : p2;
}
int cmp_up(const void* p1, const void* p2) {
	return *(int*)p1 - *(int*)p2;
}
int cmp_down(const void* p1, const void* p2) {
	return *(int*)p2 - *(int*)p1;
}
int gcd(int a, int b) {
	return a % b == 0 ? b : gcd(b, a % b);
}
int lowbit(int x) {
	return x & (-x); 
}
ll qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

int main() {
    int n, k;
    scanf("%d %d", &n, &k);
    if (k % 2 != 0) {
        printf("-1\n");
        return 0;
    }
    int a[n][n];
    memset(a, 0, sizeof(a));
    for (int i = 0; i < k / 2; i++) {
        a[i][i] = 1; // 对角线位置设置为 1
    }
    for (int i = 0; i < n; i++) {
       for(int j = 0; j < n; j++) {
           printf("%d", a[i][j]);
       }
        printf("\n");
    }
    return 0;
}

D 小苯的重排构造


链接:https://ac.nowcoder.com/acm/contest/115861/D


这道题很有意思,首先特判将不合法的剔除,主要还是要知道最终目的:(num2 - 1) * 2 + num1 + p) == k (p 是 num2 转换为 num1 的个数,其实就是1,2 交替的个数)。代码的实现很优美,值得反复欣赏。


code

#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
#include<limits.h>
#define MOD 1000000007
#define mod 998244353
#define INF 0x3f3f3f3f
typedef long long ll;  
typedef unsigned long long ull;

// 自定义随机数生成器,避免与标准库冲突
ull xorshift64star(void) {
    static ull state = 234;
    state ^= state >> 12;
    state ^= state << 25;
    state ^= state >> 27;
    return state * 0x2545F4914F6CDD1DULL;
}
int max_(int p1, int p2) {
	return p1 > p2 ? p1 : p2;
}
int min_(int p1, int p2) {
	return p1 < p2 ? p1 : p2;
}
int cmp_up(const void* p1, const void* p2) {
	return *(int*)p1 - *(int*)p2;
}
int cmp_down(const void* p1, const void* p2) {
	return *(int*)p2 - *(int*)p1;
}
int gcd(int a, int b) {
	return a % b == 0 ? b : gcd(b, a % b);
}
int lowbit(int x) {
	return x & (-x); 
}
ll qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

int main() {
    int n, k;
    scanf("%d %d", &n, &k);
    int num1 = 0, num2 = 0;
    for(int i = 0; i < n; i++) {
        int num;
        scanf("%d", &num);
        if(num == 1) num1++;
        else num2++;
    }
    
    if(num2 == 0) {
        if(n - 1 == k) {
            for(int i = 0; i < n; i++) printf("1 ");
        } else printf("-1");
        return 0;
    }
    if(num1 == 0) {
        if((n - 1) * 2 == k) {
             for(int i = 0; i < n; i++) printf("2 ");
        } else printf("-1");
        return 0;
    
    int max_k = (num2 - 1) * 2 + num1;
    int min = min_(num1, num2), max = max_(num1, num2);
    int min_k;
    if(num1 >= num2 - 1) min_k = n - 1;
    else min_k = (num2 - 1) * 2;
    if(k < min_k || k > max_k) {
        printf("-1");
        return 0;
    }
        
    int p = 0;
    while(((num2 - 1) * 2 + num1 + p) != k) {
        num2--;
        p++;
    }
    
    for(int i = 0; i < n; i++) {
        while(num2) {
            printf("2 ");
            num2--;
            i++;
        }
        printf("1 ");
        if(p) {
            printf("2 ");
            p--;
            i++;
        }
    }
    return 0;
}

E 小苯的xor图


链接:https://ac.nowcoder.com/acm/contest/115861/E


这道题我用 链式前向星 装载图(邻接表),然后暴力遍历,发现时间复杂度太大了,超时了,过了50%数据:

code

#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
#include<limits.h>
#define MOD 1000000007
#define mod 998244353
#define INF 0x3f3f3f3f
typedef long long ll;  
typedef unsigned long long ull;

// 自定义随机数生成器,避免与标准库冲突
ull xorshift64star(void) {
    static ull state = 234;
    state ^= state >> 12;
    state ^= state << 25;
    state ^= state >> 27;
    return state * 0x2545F4914F6CDD1DULL;
}
int max_(int p1, int p2) {
	return p1 > p2 ? p1 : p2;
}
int min_(int p1, int p2) {
	return p1 < p2 ? p1 : p2;
}
int cmp_up(const void* p1, const void* p2) {
	return *(int*)p1 - *(int*)p2;
}
int cmp_down(const void* p1, const void* p2) {
	return *(int*)p2 - *(int*)p1;
}
int gcd(int a, int b) {
	return a % b == 0 ? b : gcd(b, a % b);
}
int lowbit(int x) {
	return x & (-x); 
}
ll qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

#define max_n 200000

int head[max_n + 1];
int next[max_n * 2 + 1];
int to[max_n * 2 + 1];
int cnt;

void add(int x, int y) {
    next[++cnt] = head[x];
    head[x] = cnt;
    to[cnt] = y;
}

void built(int n, int m) {
    for(int i = 0; i < m; i++) {
        int x, y;
        scanf("%d %d", &x, &y);
        add(x, y);
        add(y, x);
    }
}

void init() {
    cnt = 0;
    memset(head, 0, sizeof(head));
    memset(next, 0, sizeof(next));
    memset(to, 0, sizeof(to));
}

void SUM(int* a, int len, ll* sum, int* w, int k) {
    for(int i = 0; i < len - 1; i++) {
        int x = a[i];
        for(int j = i + 1; j < len; j++) {
            int y = a[j];
            *sum = (*sum + (w[x] ^ w[k] ^ w[y])) % mod;
        }
    }
}

int main() {
    init();
    int n, m;
    scanf("%d %d", &n, &m);
    int w[n + 1];
    for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
    built(n, m);
    ll sum = 0;
    for(int i = 1; i <= n; i++) {
        int k = head[i];
        int temp[max_n];
        int index = 0;
        for(; k != 0; k = next[k]) {
            temp[index++] = to[k];
        }
        if(index >= 2) SUM(temp, index, &sum, w, i);
    }
    printf("%lld", sum);
    return 0;
}

所以需要优化 SUM 函数!利用 数学(概率思想) + 位运算 优化。很妙,需要经常温习!

在这里插入图片描述


code

#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<math.h>
#include<limits.h>
#define MOD 1000000007
#define mod 998244353
#define INF 0x3f3f3f3f
typedef long long ll;  
typedef unsigned long long ull;

// 自定义随机数生成器,避免与标准库冲突
ull xorshift64star(void) {
    static ull state = 234;
    state ^= state >> 12;
    state ^= state << 25;
    state ^= state >> 27;
    return state * 0x2545F4914F6CDD1DULL;
}
int max_(int p1, int p2) {
	return p1 > p2 ? p1 : p2;
}
int min_(int p1, int p2) {
	return p1 < p2 ? p1 : p2;
}
int cmp_up(const void* p1, const void* p2) {
	return *(int*)p1 - *(int*)p2;
}
int cmp_down(const void* p1, const void* p2) {
	return *(int*)p2 - *(int*)p1;
}
int gcd(int a, int b) {
	return a % b == 0 ? b : gcd(b, a % b);
}
int lowbit(int x) {
	return x & (-x); 
}
ll qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

#define max_n 200000

int head[max_n + 1];
int next[max_n * 2 + 1];
int to[max_n * 2 + 1];
int cnt;

void add(int x, int y) {
    next[++cnt] = head[x];
    head[x] = cnt;
    to[cnt] = y;
}

void built(int n, int m) {
    for(int i = 0; i < m; i++) {
        int x, y;
        scanf("%d %d", &x, &y);
        add(x, y);
        add(y, x);
    }
}

void init() {
    cnt = 0;
    memset(head, 0, sizeof(head));
    memset(next, 0, sizeof(next));
    memset(to, 0, sizeof(to));
}

void SUM(int* a, int len, ll* sum, int* w, int k) {
    int cnt0[32] = {0}; 
    int cnt1[32] = {0}; 
    for (int i = 0; i < len; i++) {
        int val = w[a[i]];  
        for (int b = 0; b < 32; b++) {  
            if ((val >> b) & 1) {
                cnt1[b]++;
            } else {
                cnt0[b]++;
            }
        }
    }

    int center_val = w[k];
    for (int b = 0; b < 32; b++) {
        ll c0 = cnt0[b];
        ll c1 = cnt1[b];
        ll valid_pairs;
        if ((center_val >> b) & 1) {
            // 中心位为 1 时,只有邻接点对在该位相同,异或结果才会让该位为 1
            // 有效对数量 = C(cnt0, 2) + C(cnt1, 2)
            valid_pairs = (c0 * (c0 - 1) / 2) + (c1 * (c1 - 1) / 2);
        } else {
            // 中心位为 0 时,只有邻接点对在该位不同,异或结果才会让该位为 1
            // 有效对数量 = cnt0 * cnt1
            valid_pairs = c0 * c1;
        }
        *sum = (*sum + valid_pairs * (1LL << b) % mod) % mod;
    }
}

int main() {
    init();
    int n, m;
    scanf("%d %d", &n, &m);
    int w[n + 1];
    for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
    built(n, m);
    ll sum = 0;
    for(int i = 1; i <= n; i++) {
        int k = head[i];
        int temp[max_n];
        int index = 0;
        for(; k != 0; k = next[k]) {
            temp[index++] = to[k];
        }
        if(index >= 2) SUM(temp, index, &sum, w, i);
    }
    printf("%lld", sum);
    return 0;
}

F 小苯的前缀gcd构造


链接:https://ac.nowcoder.com/acm/contest/115861/F


还是有难度的,通过 剪枝 + 预处理 大幅度优化,枚举 gcd 进行深搜,思想以及代码实现很值得学习,题目质量很高!


code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 预先生成每个数的因数列表,g[i] 存储 i 的所有因数(除自身外,从小到大)
int g[51][51];
int g_len[51];

// 初始化因数列表
void init_g() {
    for (int i = 1; i <= 50; i++) {
        g_len[i] = 0;
        for (int j = 1; j <= i; j++) {
            if (i % j == 0) {
                g[i][g_len[i]++] = j;
            }
        }
    }
}

// 深度优先搜索函数,寻找满足条件的数组
int find(int n, int m, int x, int *result, int index, int current_sum, int current_gcd) {
    if (index == n) return current_sum == x ? 1 : 0;

    // 剪枝:如果当前已选元素个数为 index,剩余需要选 n - index 个元素
    int remaining = n - index;
    int max_possible = current_sum + current_gcd * remaining;
    int min_possible = current_sum + 1 * remaining;
    if (max_possible < x || min_possible > x) {
        return 0;
    }

    // 遍历当前可能的因数
    for (int i = 0; i < g_len[current_gcd]; i++) {
        int next_gcd = g[current_gcd][i];
        int next_num = next_gcd;
        result[index] = next_num;
        if (find(n, m, x, result, index + 1, current_sum + next_gcd, next_gcd)) {
            return 1;
        }
    }
    return 0;
}

int main() {
    init_g();
    int T;
    scanf("%d", &T);
    while (T--) {
        int n, m, x;
        scanf("%d %d %d", &n, &m, &x);
        
        // 剪枝条件1:如果 x < n,直接无解,因为每个元素至少贡献 1(gcd 至少是 1 ,对应元素至少是 1 )
        if (x < n) {
            printf("-1\n");
            continue;
        }
        // 剪枝条件2:如果 x % n == 0,且 x / n <= m ,直接构造全 x/n 的数组
        int avg = x / n;
        if (x % n == 0 && avg <= m) {
            for (int i = 0; i < n; i++) {
                printf("%d ", avg);
            }
            printf("\n");
            continue;
        }
        
        int *result = (int *)malloc(n * sizeof(int));
        // 调整初始 current_gcd 的尝试范围,从可能的较大值开始,增加找到解的概率
        // 这里先尝试从 min(m, x) 开始往下找合适的初始 gcd
        int initial_gcd;
        for (initial_gcd = fmin(m, x); initial_gcd >= 1; initial_gcd--) {
            memset(result, 0, n * sizeof(int)); 
            if (find(n, m, x, result, 0, 0, initial_gcd)) {
                for (int i = 0; i < n; i++) {
                    printf("%d ", result[i]);
                }
                printf("\n");
                free(result);
                goto here; // 找到解,处理下一个测试用例
            }
        }
        
        // 所有初始 gcd 尝试都失败,输出 -1
        printf("-1\n");
        free(result);
        here:;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值