2017多校七 02, 05, 08, 10, 11

34 篇文章 0 订阅
4 篇文章 0 订阅

02 hdu 6121

题意

给定一棵 k 叉树,求其所有子树大小的异或和。

参考

http://blog.csdn.net/wubaizhe/article/details/77248527 ——WuBaizhe

想法

记当前的 k 叉树高度为 h ;
根的所有孩子,最多只有一个不是满 k 叉树,高度为 h1 ;
左右两边都是满 k 叉树,高度分别为 h1 h2 .
递归处理即可。

注意点

k == 1时,退化成一条链,即求 1 ^ 2 ^ 3 ^ ... ^n,可打表找规律。

Code

#include <bits/stdc++.h>
#define maxn 110
typedef long long LL;
LL sz[maxn], x[maxn], n, k;
LL query(LL n, LL dep) {
//    printf("%lld %lld\n", n, dep);
    if (dep == 2) return n ^ ((n - 1 & 1) ? 1 : 0);
    LL mod = pow(k, dep - 2);
    LL ful1 = (n - sz[dep - 1]) / mod;
    LL ful2 = k - ful1, ret = n ^ ((ful1 & 1) ? x[dep - 1] : 0);
    if ((n - sz[dep - 1]) % mod) {
        --ful2;
        ret ^= ((ful2 & 1) ? x[dep - 2] : 0) ^ query(n - 1 - ful1 * sz[dep - 1] - ful2 * sz[dep - 2], dep - 1);
    }
    else ret ^= ((ful2 & 1) ? x[dep - 2] : 0);
    return ret;
}
void work() {
    scanf("%lld%lld", &n, &k);
    if (k == 1) {
        LL xx = n % 4;
        LL ans;
        switch (xx) {
            case 0: ans = n; break;
            case 1: ans = 1; break;
            case 2: ans = n + 1; break;
            case 3: ans = 0;
        }
        printf("%lld\n", ans);
        return;
    }
    LL cnt = 1, cur = n, h = 1;
    sz[1] = 1, x[1] = 1;
    while (true) {
        cur -= cnt;
        cnt *= k;
        sz[h + 1] = sz[h] * k + 1;
        x[h + 1] = ((k & 1) ? x[h] : 0) ^ sz[h + 1];
        ++h;
        if (cur < cnt) break;
    }
    printf("%lld\n", query(n, h));
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}

05 hdu 6124

题意

给定 a ,要求 a 模一个数有多少种可能的结果。

思路

考虑从 a 开始往下模,最多模到一半的位置,之后得到的答案必为前面的子集。

注意

分奇偶讨论。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
void work() {
    int n;
    scanf("%d", &n);
    int ans = 0;
    if (n & 1) ans = n - (n + 1) / 2 + 1 + 1;
    else ans = n - n / 2 + 1;
    printf("%d\n", ans);
}
int main() {
//    freopen("in.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}

08 hdu 6127

题意

给定平面上 n 个点,每个点有权值 vali ,两两之间连成线段,线段的权重为其两个端点的权值乘积。现作一条穿过原点的直线,与一些线段相交,求其穿过的线段的权重之和的最大值。

参考

http://blog.csdn.net/trancyqing/article/details/77234510

官方题解

对于一条直线,线段权值和实际上就等于其两边点权和的乘积,所以把所有点按极角排个序,然后扫一圈就好了。

Code

#include <bits/stdc++.h>
#define eps 1e-6
#define PI acos(-1.0)
#define maxn 50010
typedef long long LL;
using namespace std;
struct node {
    int x, y;
    double a;
    LL v;
    bool operator < (const node nd) const {
        return a < nd.a;
    }
}p[maxn];
void work() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d%d%lld", &p[i].x, &p[i].y, &p[i].v);
        if (p[i].x == 0) p[i].a = PI / 2;
        else p[i].a = atan((double)p[i].y / p[i].x);
    }
    sort(p, p + n);
//    for (int i = 0; i < n; ++i) printf("%d %d %.3f\n", p[i].x, p[i].y, p[i].a);
    LL lsum = 0, rsum = 0;
    for (int i = 0; i < n; ++i) {
        if (p[i].x >= 0) rsum += p[i].v;
        else lsum += p[i].v;
    }
    LL ans = rsum * lsum;
    for (int i = 0; i < n; ++i) {
        if (p[i].x >= 0) {
            rsum -= p[i].v;
            lsum += p[i].v;
        }
        else {
            lsum -= p[i].v;
            rsum += p[i].v;
        }
        ans = max(ans, rsum * lsum);
    }
    printf("%lld\n", ans);
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}

10 hdu 6129

题意

对给定的数组 a[] 求 m 次前缀异或,输出最终的数组。

参考

http://blog.csdn.net/mengxiang000000/article/details/77200451

想法

找规律。也要找对路子。
考虑第一个元素对后面第 i 行第 j 列的贡献,发现次数为杨辉三角(不要忙着把偶数次消掉,这样很难看出规律)。
i 行第 j 列: C(i+j2,i1)

注意点

乍一看是 n2 的,事实上 第一个元素对第 j 列的贡献次数 就相当于 第二个元素对第 j+1 列的贡献次数,就相当于第 i 个元素对第 i+j1 的贡献次数,所以一旦在判断 第一个元素对第 j 列的贡献次数 时发现为偶数,之后的就都没有必要再判断了。
至于判断组合数的奇偶:Lucas定理
对于 C(n,m) n 0 的位置, m 也必须为 0,即 (n & m) == m

反思

一开始找规律找歪了,每四次前四个循环,每八次前八个循环,每十六次前十六个循环,然而按照这规律写还是 n2 的,T到最后。

Code

#include <bits/stdc++.h>
#define maxn 200010
using namespace std;
int n,m, a[maxn], b[maxn], ans[maxn];
void work() {
    scanf("%d %d", &n, &m);
    memset(ans, 0, sizeof(ans));
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    int mod = 1;
    while (mod < n) mod <<= 1;
    m %= mod;
    for (int i = 1; i <= n; ++i) {
        int deno = m + i - 2, nomi = i - 1;
        if ((deno & nomi) == nomi) {
            for (int j = 1; j <= n - i + 1; ++j) ans[i + j - 1] ^= a[j];
        }
    }
    printf("%d", ans[1]);
    for (int i = 2; i <= n; ++i) {
        printf(" %d", ans[i]);
    }
    printf("\n");
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}

10 hdu 6130

题意

序列一: 1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1,2,2,1,...
序列二: 1,22,11,2,1,22,1,22,11,2,11,22,1,...
将序列一分组得到序列二,将序列二中每组的元素个数写下来得到序列一。

思路

直接模拟即可,用序列一生成序列二。

Code

#include <bits/stdc++.h>
#include <vector>
#define maxn 10000000
int a[maxn + 10];
using namespace std;
vector<int> G;
typedef long long LL;
void init() {
    int tot = 2, i = 2, j = 2;
    a[0] = 1, a[1] = 2, a[2] = 2;
        while (i >= j) {
            int temp = a[j];
            if (temp == 1) {
                a[i + 1] = 3 - a[i];
                i += 1;
            }
            else {
                    a[i + 1] = a[i + 2] = 3 - a[i];
                    i += 2;
            }
            j += 1;
        if (i >= 1e7) break;
        }
}
void work() {
    int n;
    scanf("%d", &n);
    printf("%d\n", a[n-1]);
}
int main() {
//    freopen("in.txt", "r", stdin);
    init();
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值