【cf】Codeforces Round #774 (Div. 2) 前4题

12 篇文章 0 订阅
这篇博客探讨了几道算法题目,包括基于简单数学的SquareCounting问题,排序策略的QualityvsQuantity,使用状态压缩DP和位运算的FactorialsandPowersofTwo,以及涉及树形DP的WeighttheTree。文章详细介绍了每道题目的大意、解决方案和代码实现,旨在帮助读者理解并掌握这些算法技巧。
摘要由CSDN通过智能技术生成

许多年没打cf了,偶尔打了一盘,恢复紫名了。

A. Square Counting 简单数学

题目链接 A. Square Counting

题目大意

一个(n+1)长度的数列,每个值属于[0,n)∪{n2},总和为s
给定n和s,求有多少个数列中的数值是n^2。保证存在答案,可以证明当答案存在时答案唯一。

题解

因为[0,n)范围内的数至多有(n+1)个,最小总值为0,最大总值为n2-1,所以数列中的n2不能被[0,n)范围内的数代替,因此若答案存在一定唯一。答案为s / (n2)。

时间O(T)

代码

#include <bits/stdc++.h>

using namespace std;
using LL = long long;

int main() {
    int T;
    cin >> T;
    while (T--) {
        LL n,x;
        cin>>n>>x;
        cout<<(x/(n*n))<<endl;

    }
    return 0;
}

B. Quality vs Quantity 排序

题目大意

对数列进行染色,可以染成红色、蓝色或者不染。求能不能使“被染红色的数值之和sum”大于“被染蓝色的数值之和sum”,同时“被染红色的数字个数count”小于“被染蓝色的数值个数count”。

题解

sum count意思像是SQL的聚合函数
因为数值为非负数,可以不染色,如果有个数差很多的答案,肯定也有个数值差1个的答案(反之不一定)。
若不染蓝色,蓝色sum不可能严格大于红色sum。很容易想到一种个数只差一个的情况,染2个红色,1个蓝色。显然应该选最小的两个数染成红色,最大的一个数染成蓝色,如果这样不行其它染法肯定更不行。
进一步,每次选一个未染色最大数染成蓝色,一个未染色最小数染成红色,计算是否成立。注意不要重叠,一个数最多只能染一个颜色。
过程中只要有一个成立,返回YES,否则返回NO。
时间O(nlogn)

代码

#include <bits/stdc++.h>

using namespace std;
using LL = long long;
int a[201000];
int main() {
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        sort(a+1,a+n+1);
        LL lsum = a[1], rsum = 0;
        bool ok = false;
        for(int i=2,j=n;i<j;i++,j--){
            lsum += a[i];
            rsum += a[j];
            if(lsum<rsum){
                ok=true;
                break;
            }
        }
        puts(ok?"YES":"NO");
    }
    return 0;
}

C. Factorials and Powers of Two 状态压缩dp+位运算

题目大意

将一个数表示成若干(尽可能少)个数的和,这若干个数要么是阶乘,要么是2的幂。

题解

显然必然有答案。将一个数表示成多个2的幂的和,显然最少有(该数二进制表示中1的个数)个数字。因为n≤1012,所以最多40个数字。
证明一种阶乘最多出现一次??好吧我是用数据规模反推的
数可以分为若干个阶乘之和 和 若干个2的幂之和。因为一种阶乘只能出现一次,预处理各种阶乘的和,类似状态压缩dp。枚举这部分,每次阶乘的和的状态中的二进制表示中1的位数,加上原数减去阶乘的和的二进制表示中1的位数,求和就是一种答案。求所有答案的最小值,输出。

代码

#include <bits/stdc++.h>

using namespace std;
using LL = long long;
LL jc[15];
LL sum[40000];

LL lowbit(LL x) {
    return x & -x;
}

int getbit(LL x) {
    int ans = 0;
    while (x) {
        ans++;
        x -= lowbit(x);
    }
    return ans;
}

int getlowpos(LL x) {
    LL low = lowbit(x);
    int tot = 0;
    while (low) {
        if (low == (1 << tot))break;
        ++tot;
    }
    return tot;
}

int main() {
    int T;
    cin >> T;
    jc[0] = 1;
    for (int i = 1; i < 15; i++)jc[i] = jc[i - 1] * i;
    for (int i = 1; i < (1LL << 15); i++) {
        sum[i] = sum[i - lowbit(i)] + jc[getlowpos(i)];
    }

    while (T--) {
        LL n;
        cin >> n;
        int ans = 1e9 + 7;
        for (int aim = 0; aim < (1LL << 15); aim++) {
            if (n < sum[aim])continue;
//            clog << "aim " << aim << endl;
            int x = getbit(n - sum[aim]);
//            clog << "x " << x << endl;
            int y = getbit(aim);
//            clog << "y " << y << endl;
            ans = min(ans, x + y);
//            clog << "ans " << ans << endl;
        }
        cout << ans << endl;
    }
    return 0;
}

D. Weight the Tree 树形dp+dfs

题目大意

给树的节点赋正数权值,计算权值等于相邻节点权值之和相等的结点(好节点)个数,让这个个数最大。在这个前提下,让所有节点的权值总和最小。

题解

因为最少有两个节点。只有两个节点是特殊情况,特判。
其它情况下,好节点不会相邻。因为两个好节点,至少有一个还有其它相邻节点,权值至少为1,与两个都是好节点矛盾。
树形dp枚举每个节点自己是否是好节点,计算子树的最大好节点数,和此时的最小权值总数。
子树根节点是好节点,子节点不能是好节点。根节点不是好节点,子节点可以是也可以不是好节点,按“最大好节点数,和此时的最小权值总数”取最大值。
从1开始树形dp,求出1是否应该是好节点,设置节点1“好节点状态”。再dfs一遍整理方案,在进入子树前检查当前最佳答案是以子树是否是好节点,设置子节点的“好节点状态”。
输出答案

代码

#include <bits/stdc++.h>

using namespace std;
using LL = long long;
const int N = 2e5 + 1000;
int n;
int eh[N], ev[N * 2], en[N * 2], et;

struct Ans {
    int count, sum;

    Ans() : Ans(0, 0) {}

    Ans(int count, int sum) : count(count), sum(sum) {}

    bool operator<(const Ans y) const {
        return count < y.count || (count == y.count && sum > y.sum);
    }
};

Ans dp[N][2];
int deg[N];
bool vis[N];

void add(int u, int v) {
    ev[++et] = v;
    en[et] = eh[u];
    eh[u] = et;
}

void dfs(int u, int fa) {
    dp[u][0] = Ans(0, 1);
    dp[u][1] = Ans(1, 0);
    for (int i = eh[u]; i; i = en[i]) {
        deg[u]++;
        if (ev[i] == fa) continue;
        dfs(ev[i], u);
        dp[u][0].count += max(dp[ev[i]][0], dp[ev[i]][1]).count;
        dp[u][0].sum += max(dp[ev[i]][0], dp[ev[i]][1]).sum;
        dp[u][1].count += dp[ev[i]][0].count;
        dp[u][1].sum += dp[ev[i]][0].sum;
    }
    dp[u][1].sum += deg[u];
//    clog << "dp [" << u << "][0] " << dp[u][0].count << " " << dp[u][0].sum << endl;
//    clog << "dp [" << u << "][1] " << dp[u][1].count << " " << dp[u][1].sum << endl;
}

void check(int u, int fa) {
    for (int i = eh[u]; i; i = en[i]) {
        if (ev[i] == fa) continue;
        if (vis[u]) {
            vis[ev[i]] = false;
        } else {
            if (dp[ev[i]][0] < dp[ev[i]][1]) {
                vis[ev[i]] = true;
            } else {
                vis[ev[i]] = false;
            }
        }
        check(ev[i], u);
    }
}

int main() {
//    freopen("D:\\C++\\codeforces\\in.txt", "r", stdin);
    cin >> n;
    if(n==2){
        cout<<"2 2\n";
        cout<<"1 1\n";
        return 0;
    }
    for (int i = 1, u, v; i < n; i++) {
        scanf("%d%d", &u, &v);
        add(u, v);
        add(v, u);
    }
    dfs(1, 0);
    if (dp[1][0] < dp[1][1]) {
        vis[1] = true;
        cout << dp[1][1].count << " " << dp[1][1].sum << endl;
    } else {
        cout << dp[1][0].count << " " << dp[1][0].sum << endl;
    }
    check(1, 0);
    for (int i = 1; i <= n; i++) {
        cout << (vis[i] ? deg[i] : 1) << " ";
    }
    cout << endl;
    return 0;
}

E. Power Board 看起来像是数论?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值