CSP模拟7

A. 卷

一道可爱的树形 DP 喵!

题目保证了 \(w_i\) 是在给定范围内随机生成的,所以不会炸精度。

首先明确题意,是求出最大乘积独立集之后取模,而不是边乘边取模。边乘边取模会炸,例如 \(10^9 +8\)\(10^9+ 7\) 取模后小于 \(2\),但显然 \(10^9 + 8 > 2\)

那怎么办呢?高精?

可以用我们对数离散,由于

\[\log{a \times b}=\log a + \log b \]

所以,我们可以把乘法转化为加法,开一个数组 \(f\) 存储未取模的结果用对数表示,用它来判断大小,再用数组 \(dp\) 储存答案。

记得开 \(\text{long double}\)\(\text{long long}\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>

using namespace std;

#define int long long

const int N = 200500;
const int Mod = 1e9 + 7;

// Graph
struct Edge{
    int next,to;
}e[N << 2];

int h[N],cnt;

void Add(int u,int v) {
    cnt ++;
    e[cnt].next = h[u];
    h[u] = cnt;
    e[cnt].to = v;

    return ;
}

int n,w[N];

long double f[N][2];
int dp[N][2];

// DP 
void dfs(int x,int fa) {
    f[x][1] = log(w[x]);
    f[x][0] = 0;
    dp[x][1] = w[x] % Mod;
    dp[x][0] = 1;

    for(int i = h[x];i; i = e[i].next) {
        int to = e[i].to;

        if(to == fa)
            continue;
        
        dfs(to,x);

        if(f[to][0] > f[to][1])
            dp[x][0] = dp[x][0] * dp[to][0] % Mod;
        else   
            dp[x][0] = dp[x][0] * dp[to][1] % Mod;

        f[x][0] += max(f[to][0],f[to][1]);
        f[x][1] += f[to][0];
        dp[x][1] *= dp[to][0];
        dp[x][1] %= Mod;
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1;i <= n; i++) {
        cin >> w[i];
    }
    for(int i = 1,u,v;i < n; i++) {
        cin >> u >> v;
        Add(u,v);
        Add(v,u);
    }

    dfs(1,0);

    if(f[1][0] > f[1][1])
        cout << dp[1][0];
    else    
        cout << dp[1][1];
    return 0;
}

B. 简单题

首先我们可以把这个集合拆成很多条链,每条链的形式都是

\[i \rightarrow i \times 2^1 \rightarrow i \times 2^2 \rightarrow \cdots \rightarrow i \times 2^k$$的形式。 这些链的开头都是奇数(因为偶数肯定是其他数的 $2$ 倍),链长都是 $\log$ 级别。 对于一条链,一定是隔一个选一个,把它分成 $A$ 和 $B$ 两个集合。 对于偶数长的链,两部分的长度相等,都为 $\dfrac{len}{2}$; 对于奇数长的链,其中一部分会比另一部分多 $1$,二者为 $\left\lfloor\dfrac{len}{2}\right\rfloor$ 和 $\left\lfloor\dfrac{len}{2}\right\rfloor +1$。 所以我们可以发现,集合 $A$ 的大小一定大于所有偶数链长度和所有奇数链长度较短的一部分长度的和,这部分我们设为 $base$。 然后就是选择链中部分的方法,偶数链要么从第一个数开始选,要么从第二个数开始选,共两种方法,即 $2^{cnt}$ 种。 奇数链的方案数就是从奇数链数量中选取 $m - base$ 条链的方案数,要用卢卡斯定理。 然后就是要求各个长度的链的个数了。 设一条链的长度为 $i + 1$,开头数为 $x$,所以除了 $x$ 以外链中有 $i$ 个数。 我们可以得到 $$x \cdot 2^i \leq n < x \cdot 2^{i + 1}\]

变化一下,我们得到

\[\dfrac{n}{2^{i+1}} < x \leq \dfrac{n}{2^i} \]

由于链都是以奇数为开头的,所以,对于长度为 \(i + 1\) 的链,链的数量为 \(x\) 所在区间中奇数的个数。

所以我们可以枚举链的长度,然后计算出区间内奇数数量,然后得到该长度下链的数量。

这道题就可以完结了。

咕咕咕

posted @ 2023-07-27 19:39  -白简-  阅读(9)  评论(0编辑  收藏  举报