首页 > 其他分享 >CF1967C题解

CF1967C题解

时间:2024-05-08 17:48:35浏览次数:23  
标签:下标 CF1967C 题解 long int 节点 dp define

CF1906D

这里更容易进入且有翻译

题意

对于数组 \(a\),有函数 \(f(a)\),设数组 \(s = f(a)\),则对于每一个 \(s_i\),都有:

\[s_i = \left(\sum^{i}_{j = (i \operatorname{\& } (i - 1)) + 1}{a_j}\right) \]

其中 \(\&\) 表示按位与运算。

对于一个正整数 \(k\),函数 \(f^k(a)\) 定义如下:

\[ f^k(a)= \begin{cases} f(a)&k=1\\\\ f(f^{k-1}(a))&k > 1 \end{cases}\]

给你正整数 \(n, k\) 和一个长度为 \(n\) 的数组 \(b\) 表示 \(f^k(a)\),求一个符合题意的数组 \(a\),可以证明答案总是存在的。

解析

理解题意便可以看出,各个下标数值之间的关系形成一片森林,下标 \(\operatorname{lowbit}\) 值(二进制形式下数字为 1 的最低数位对应值,如 \(\operatorname{lowbit}(12) = 4\))越小,其对应节点在树上深度越大。明显每个 \(a_i\) 仅对其自身和在该树上的祖先有贡献,且对自身的贡献为 1,对其有贡献的仅有自身及其后代。

image

打表可看出对于下标 \(i\) 和其祖先 \(j\),贡献 \(c_{i, j}\) 的大小仅与下标 \(i, j\) 在树上的节点深度差有关,我们可以预处理在 \(k\) 次函数计算后不同深度差下后代节点对祖先节点的贡献。设 \(dp_{i, j}\) 表示深度差为 \(i\) 时进行在 \(f^j(a)\) 中后代节点对祖先节点的贡献。初始时除了 \(dp_{1, 0} = 1\),其余 \(dp_{i, j} = 0\), 有转移方程:

\[dp_{i, j} = \sum_{l = 1}^{i}{dp_{l, j - 1}} \]

由于 \(k\) 较大但 \(i\) 的范围较小,我们可以考虑通过矩阵加速优化计算 \(dp_{i, k}\) 的过程。

预处理完贡献值后,自底向上倒推,在祖先节点中减去当前节点的贡献,最后该祖先节点 \(i\) 保留的值就是原本 \(a_i\) 的值。复杂度大概为 \(O({\log}^3{n} \log{k} + n\log{n})\)

代码

#include <bits/stdc++.h>
#include <unordered_map>
#define LL long long
#define ll long long
#define double long double
#define pii pair<int, int>
#define pll pair<LL, LL>
#define pdd pair<double, double>

using namespace std;

//nxt数组用于记录下标的父节点下标,N为矩阵大小
LL a[200005], b[200005], pre[25][25], nxt[200005], N, mod = 998244353;

//矩阵加速
void mqpow(int n)
{
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= N; j++)
            pre[i][j] = (i == j);

    LL M[25][25], cur[25][25];
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= N; j++)
            M[i][j] = (i >= j);

    while (n)
    {
        if (n & 1)
        {
            for (int i = 1; i <= N; i++)
                for (int j = 1; j <= N; j++)
                    cur[i][j] = 0;
            for (int i = 1; i <= N; i++)
                for (int j = 1; j <= N; j++)
                    for (int k = 1; k <= N; k++)
                        cur[i][j] = (cur[i][j] + pre[i][k] * M[k][j] % mod) % mod;
            for (int i = 1; i <= N; i++)
                for (int j = 1; j <= N; j++)
                    pre[i][j] = cur[i][j];
        }
        for (int i = 1; i <= N; i++)
            for (int j = 1; j <= N; j++)
                cur[i][j] = 0;
        for (int i = 1; i <= N; i++)
            for (int j = 1; j <= N; j++)
                for (int k = 1; k <= N; k++)
                    cur[i][j] = (cur[i][j] + M[i][k] * M[k][j] % mod) % mod;
        for (int i = 1; i <= N; i++)
            for (int j = 1; j <= N; j++)
                M[i][j] = cur[i][j];

        n >>= 1;
    }
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    //预处理nxt数组
    for (int i = 1; i <= 200000; i++)
    {
        int idx = 0, cur = i;
        while (!(cur & (1 << idx)))
            cur |= 1 << idx++;
        while (cur & (1 << idx))
            idx++;
        nxt[i] = cur ^ ((1 << (idx + 1)) - 1);
    }

    int T, n;
    LL k;
    cin >> T;
    while (T--)
    {
        cin >> n >> k;
        N = log2(n) + 1;
        for (int i = 1; i <= n; i++)
            cin >> b[i];

        mqpow(k); //预处理深度差产生的贡献系数
        for (int i = 1; i <= N; i++)
            for (int j = 1; j * (1 << (i - 1)) <= n; j += 2) //从叶子节点(lowbit值为1下标)开始倒推
            {
                int cur = j * (1 << (i - 1));
                a[cur] = b[cur]; //节点保留值即为答案
                for (int l = nxt[cur], m = 2; l <= n; l = nxt[l], m++)
                    b[l] = (b[l] + mod - a[cur] * pre[m][1] % mod) % mod; //从祖先节点中减去当前节点的贡献
            }

        for (int i = 1; i <= n; i++)
            cout << a[i] << " ";
        cout << "\n";
    }
}

最后祝各位顺利AC。>w<

标签:下标,CF1967C,题解,long,int,节点,dp,define
From: https://www.cnblogs.com/ItsAiHAn/p/18180069

相关文章

  • P10429 [蓝桥杯 2024 省 B] 拔河 题解
    思路通过动态规划计算出所有连续子序列的力量值之和,并将其存储在一个数组中然后排序,遍历一遍数组,找到相邻两个力量值之和的差的绝对值的最小值,然后输出这个答案就行了。时间复杂度大概是\(O(n^2\logn)\)。来个python的代码defmin_power_diff(n,a):#计算所有连续子序列......
  • 「高精度乘法+高精度加法」P10425 [蓝桥杯 2024 省 B] R 格式 题解
    解题思路题意分析:将浮点数乘以\(2^n\);四舍五入到最接近的整数。根据题意将\(d\times2^n\)分解为\(d\times2\times2\times2\times2……\),因为\(d\)长度小于等于\(1024\),所以可以使用高精度乘法的算法来实现一个小数乘以一个大于\(0\)的整数时,小数点位数本身不会......
  • [COCI2022-2023#1] Berilij 题解
    SolutionP9030[COCI2022-2023#1]Berilij本题解转载翻译自官方题解:COCI2022/2023CONTEST1Part1让我们定义图形\(G\),顶点代表飞船,边代表两艘飞船外部接触的情况。此外,让边的边权成为它所连接的圆之间的距离。现在的任务等同于为顶点找到非负值,使得每条边所连接的两个顶......
  • cuda使用时Could not locate zlibwapi.dll 问题解释和解决
    第一次配置cuda环境,python环境训练模型时,可能遇到Couldnotlocatezlibwapi.dll.Pleasemakesureitisinyourlibrarypath! 原因就是window系统里没有zlibwapi.dll.,与cuda没关系,cuda只是依赖它。安装某些软件时可能会自动把这个动态库安装到系统的某个path路径下,比如......
  • ICPC2024 武汉邀请赛 题解
    2024ICPCNationalInvitationalCollegiateProgrammingContest,WuhanSiteB-CountlessMeSolution显然,只能执行\(n\)次操作是没用的条件我们只需要把和\(sum\)分给\(n\)个数,使得\(n\)个数的或和最小即可从高到低考虑每一位,假设此时枚举到第\(i\)位如果这一......
  • 【题解】爬山 蓝桥杯2024省B
    题目链接:P10429[蓝桥杯2024省B]拔河[蓝桥杯2024省B]拔河题目描述小明是学校里的一名老师,他带的班级共有\(n\)名同学,第\(i\)名同学力量值为\(a_i\)。在闲暇之余,小明决定在班级里组织一场拔河比赛。为了保证比赛的双方实力尽可能相近,需要在这\(n\)名同学中挑选......
  • XMOJ 四月月赛 T3 旅行 题解
    我们首先尝试挖掘这个分组的性质。我们发现,我们可以把在同一个组的夫妻和不在同一个组的夫妻分开来处理。这里,分开之后我们只需要让一种情况有顺序,另外一种不能有顺序。如果两个没有顺序/有顺序的序列合并,一定会出现漏算/多算。所以为了方便,我们可以把第二种情况看作有顺......
  • [题解]P1757 通天之分组背包
    P1757通天之分组背包分组背包模板题。总共\(s\)组,每组最多取一个物品,实际上就是一个物品总数为\(s\)的背包。for(inti=1;i<=s;i++){//枚举组 for(intj=1;j<=n[i];j++){//枚举每组的元素 for(intk=1;k<=m;k++){//枚举背包大小 f[i][k]=max(f[i][k],f[i-1][k]); if(......
  • P9527 [JOISC2022] 洒水器 题解
    题目传送门以下设\(\operatorname{dis}(x,y)\)表示树上\(x,y\)两点间的距离。修改时对\(u\)的周围与\(u\)距离小于等于\(d\)的点的点权乘\(w\)。暴力不行,于是考虑打标记。注意到\(0\led\le40\),一个很自然的想法是:设\(tag(x,i)\)表示将\(x\)的子树内与\(x\)......
  • 数数 题解
    writeby小超手123题意:现在有四种物品,分别有\(n_{1},n_{2},n_{3},n_{4}\)个,有多少种排列物品的方案使得任意两个相邻物品的种类不同。\(n_{1},n_{2}\le200,\\n_{3},n_{4}\le50000\)。分析:可以考虑先把物品\(A,B\)排列好,再把物品\(C,D\)插入进去。需要注意的......