前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >#633 DIV2 题解

#633 DIV2 题解

作者头像
ACM算法日常
发布2020-04-22 15:38:13
2800
发布2020-04-22 15:38:13
举报
文章被收录于专栏:ACM算法日常ACM算法日常

组样例,每组样例给一个, 代表个三角形按照菱形的样子尽可能的拼凑在一起(可以旋转),如图所示

问对于每个有多少中不同的拼法(存在一个菱形,由不同的三角形覆盖则属于一种拼法)。

2
2
1
2
1

关注中间未旋转的菱形数量, 发现每次只有有一个这样的菱形可以由两个相同的三角形覆盖,同时每一个这样的菱形都可以由两个相同的三角形覆盖,且一旦这样覆盖之后,整个图没有变化的余地,所以这样的菱形个数就是答案,而对于来说会有个这样的菱形,所以直接输出即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main(){
    int t = 0;
    scanf("%d", &t);
    while(t--) {
        ll n = 0;
        scanf("%lld", &n);
        printf("%lld\n", n);
    }
    return 0;
}

组样例,每组给一个,再给个数,现在重新排列这个数,使得所有满足条件的 有。

2
6
5 -2 4 8 6 5
4
8 1 4 2
5 5 4 6 8 -2
1 2 4 8

首先将数组排序,之后按照这样的方式摆放。显然除去首尾,对于任意一个位置的数只有 或者 这两种可能。

因为是排好序的

所以,所以,同理可得。

所以倒序输出即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;

int a[maxn];
vector<int> vec;

int main(){
    int t = 0;
    scanf("%d", &t);
    while(t--) {
        int n = 0;
        vec.clear();
        scanf("%d", &n);
        for(int i = 1;i <= n;i++) {
            scanf("%d", a + i);
        }
        sort(a + 1, a + 1 + n);
        int tot1 = 1, tot2 = n;
        for(int i = 1;i <= n;i++) {
            if(i % 2 == 0) {
                vec.push_back(a[tot1++]);
            } else {
                vec.push_back(a[tot2--]);
            }
        }
        reverse(vec.begin(), vec.end());
        int len = vec.size();
        for(int i = 0;i < len;i++) {
            printf("%d%c", vec[i], i == len - 1 ? '\n' : ' ');
        }
    }
    return 0;
}

组样例,每组给一个,再给个数,进行很多轮操作,每一轮操作选取其中的一些数,将,为第轮操作。问最少需要多少轮操作,使得序列为一个非降序列。

3
4
1 7 6 5
5
1 2 3 4 5
2
0 -4
2
0
3

对于,我们需要考虑和之间的大小关系,如果,就不需要改变,否则,就需要将其变为,设之间的差为。关注到我们的操作是加,这就是在二进制表示的某一位上加1,且选数是任意的,所以我们只需要在所有的中找到二进制表示中1所在的最高位即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define mp make_pair
const int maxn = 4e5+5;
const int MOD = 1e9+7;
#define mod(x) x % MOD
#define INF 0x3f3f3f3f

int a[maxn];

int main(){
    int t = 0;
    scanf("%d", &t);
    while(t--) {
        int n = 0;
        scanf("%d", &n);
        for(int i = 1;i <= n;i++) {
            scanf("%d", a + i);
        }
        if(n == 1) {
            puts("0");
            continue;
        }
        int Max = a[1], ans = 0;
        for(int i = 2;i <= n;i++) {
            if(a[i] < Max) {
                int d = Max - a[i], res = 0;
                while(d) {
                    res++;
                    d = d / 2;
                }
                ans = max(ans, res);
            } else {
                Max = a[i];
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

给一颗个点的树,在树边上加上正整数边权使得,任意两个叶子之间简单路径的边权异或和都等于0。问树边权最少能有多少不同的数和最多能有多少不同的数

6
1 3
2 3
3 4
4 5
5 6
1 4

首先最少的很好解决,如果一个叶子和其他的任意叶子中的一个之间相隔点数为偶数,则中间会有奇数条边,用这种方式,所以答案是3

否则,所有叶子两两之间只有偶数条边,用即可,所以答案是1

之间就是考虑最多的情况了,考虑树形,为子树中最多可能的不同的边权个数。同时注意到,不管是哪个叶子,只要该叶子在子树中,其到节点的简单路径边权异或和就应该是相同的。而边权上又可以放很大的数,所以子树中的叶子到节点的简单路径边数大于等于2,则这几条边都对答案有贡献。如果只是等于1的话,由于一个数的异或和只能是的本身,所以只有其中一条对答案有贡献。同时注意的父节点也可能是叶子,特判一下只能做一次贡献。

具体转移见代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;

vector<int> G[maxn];
vector<int> leaf;
int deg[maxn], depth[maxn], dp[maxn], flag[maxn];

void dfs(int u, int fa) {
    depth[u] = depth[fa] + 1;
    bool f = 0;
    for(int i = 0;i < G[u].size();i++) {
        int v = G[u][i];
        if(v == fa) continue;
        dfs(v, u);
        if(deg[v] > 1) {
            dp[u] += dp[v] + 1;
        } else {
            if(f == 0 && flag[fa] != 1) {
                f = 1;
                dp[u]++;
            }
        }
    }
}

int main(){
    int n = 0;
    scanf("%d", &n);
    for(int i = 1;i < n;i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        G[u].push_back(v), G[v].push_back(u);
        deg[u]++, deg[v]++;
    }
    for(int i = 1;i <= n;i++) {
        if(deg[i] == 1) {
            leaf.push_back(i);
            flag[i]++;
        }
    } 
    int ans1 = 0;
    dfs(1, 0);
    for(int i = 1;i < leaf.size();i++) {
        if(abs(depth[leaf[i]] - depth[leaf[0]]) % 2 == 1) {
            ans1 = 3;
        }
    }
    if(ans1 == 0) ans1 = 1;
    printf("%d %d\n", ans1, dp[1]);
    return 0;
}

集合是一个空集, 考虑往其中加元素,找到字典序最小的三元组,有,且未在中出现过。则讲其加入,组样例,每组样例一个,输出集合中第个数。

9
1
2
3
4
5
6
7
8
9
1
2
3
4
8
12
5
10
15

第一个三元组是(1, 2, 3),第二个是(4, 8, 12)。考虑的二进制是0100,而是肯定要大于的,不然就可以互换位置,同时也要大于,所以的最高位1一定要比0100的最高位高,同时要保证尽可能小,所以为1000,这样就是1100,

做一步推广,将二进制表示的位数补齐到2的倍数时的最高两位一定是01,的最高两位一定是,的最高两位一定是,因为如果是10或者11的话,则在,必定有一个更小的,其对应的或者将这个数用过的。

在最高位确定后,其他的位数也两位两位的看的话,这个时候就未必一定是了,有可能是 ,换成四进制的话也就是0,1,2,3。

当的某一位是1时,显然字典序最小是(1, 2, 3)的组合。

当的某一位是2时,这个时候的这一位不能取1,因为之前有过(1, 2, 3),这个时候如果是(2, 1, 3)的话显然最后的3是重复的。所以只能是(2, 3, 1)。

当的某一位是3时,的这一位考虑到字典序最小取1,这样就是(3, 1, 2)也没有重复。

0的时候直接都是0即可。

最后结论:将,,换成四进制表示,然后考虑考虑每个的位数上为多少。

如果该位上为1,则b为2,c为3

其他同上述(2, 3, 1)和(3, 1, 2)

所以先从求出对应的三元组,再由对应的三元组求出,最后看对3的余数,来判断输出中的哪一个。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 50;

ll bit[maxn], a, b, c;
vector<ll> vec;

int main(){
    int t = 0;
    scanf("%d", &t);
    bit[0] = 1;
    for(int i = 1;i <= 30;i++) {
        bit[i] = bit[i - 1] * 4ll;
    }
    while(t--) {
        vec.clear();
        a = b = c = 0;
        ll n;
        scanf("%lld", &n);
        ll tmp = (n - 1ll) / 3ll + 1;
        for(int i = 0;i <= 30;i++) {
            if(tmp > bit[i]) {
                tmp -= bit[i];
            } else {
                a = bit[i] + tmp - 1; break;
            }
        }
        tmp = a;
        while(tmp) {
            vec.push_back(tmp % 4);
            tmp /= 4;
        }
        reverse(vec.begin(), vec.end());
        int len = vec.size();
        for(int i = 0;i < len;i++) {
            if(vec[i] % 4 == 1) {
                b = b + 2;
                c = c + 3;
            } else if(vec[i] % 4 == 2) {
                b = b + 3;
                c = c + 1;
            } else if(vec[i] % 4 == 3) {
                b = b + 1;
                c = c + 2;
            }
            if(i != len - 1) b = b * 4, c = c * 4;
        }

        if(n % 3 == 1) {
            printf("%lld\n", a);
        } else if(n % 3 == 2) {
            printf("%lld\n", b);
        } else {
            printf("%lld\n", c);
        }
    }    
    return 0;
}

温馨提示

如果你喜欢本文,请分享到朋友圈,想要获得更多信息,请关注ACM算法日常

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ACM算法日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档