• 【题解】CF1559E Mocha and Stars


    题目:Codeforces 链接  &  Luogu 链接

    如果只看前两个条件,那么这是一个比较显然的背包问题。

    令 $f_{i,j}$ 表示前 $n$ 个数 $a_i$ 总和为 $j$ 的方案数。

    那么对于每个 $a_i=l_i sim r_i$ 均有

    $$f_{i,j}=sum_{k=l_i}^{r_i} f_{i-1,j-k}$$

    当然直接上背包复杂度是 $mathcal{O}(nm^2)$ 级别的,我们需要把每一个 $i$ 的所有 $f_{i,j}$ 记录一个前缀和。记

    $$s_{i,j}=sum_{k=0}^{j}f_{i,k}$$

    且当 $j<0$ 时 $s_{i,j}=0$。

    那么有可以一次性转移的方程:

    $$f_{i,j}=s_{i-1,j-l_i}-s_{i-1,j-r_i-1}$$

    时间复杂度降为了 $mathcal{O}(nm)$。


    之后就是最后一个条件:$gcd(a_1,a_2,...,a_n)=1$。

    这可以让人联想到许多数论题目的常见解决办法,比如莫比乌斯反演或者容斥,这里讲讲容斥做法。

    $$F(x)=sum_{a_1=l_1}^{r_1}sum_{a_2=l_2}^{r_2}...sum_{a_n=l_n}^{r_n}[gcd(a_1,a_2,...,a_n)=x]left[sum_{i}a_i le m ight]$$

    显然我们最后要求 $F(1)$。

    观察到这个式子仍比较麻烦,但是条件转化成 $[x mid gcd(a_1,a_2,...,a_n)]$ 的话问题就会好做点。

    于是再定义 $G(x)=sumlimits_{d=1}^{lfloorfrac{m}{x} floor}F(dx)$,相当于

    $$G(x)=sum_{a_1=l_1}^{r_1}sum_{a_2=l_2}^{r_2}...sum_{a_n=l_n}^{r_n}[x mid gcd(a_1,a_2,...,a_n)]left[sum_{i}a_i le m ight]$$

    然后 $[x mid gcd(a_1,a_2,...,a_n)]$ 这个条件就等价于限制了每个 $a_i$ 都是 $x$ 的倍数

    那我们在枚举 $a_i$ 的时候直接强制它是 $x$ 的倍数,就消去 $gcd=1$ 的条件,可以直接上背包了。


    之后还没完,我们要算的是 $F(1)$,但只求出了 $G(x)$。

    观察一下 $G(x)$ 的定义式

    $$G(x)=sum_{d=1}^{lfloorfrac{m}{x} floor}F(dx)$$

    移个项:

    $$F(x)=G(x)-sum_{d=2}^{lfloorfrac{m}{x} floor}F(dx)$$

    那么我们从 $m$ 到 $1$ 倒序枚举 $x$,$G(x)$ 用背包求,且所有的 $F(x)$ 都可以求出来。

    在枚举 $x$ 和 $x$ 的倍数这一步骤,时间复杂度为调和级数级别的,大约为 $mathcal{O}(m ln m)$。

    因此均摊到每个 $x$ 的时间复杂度为 $mathcal{O}(ln m)$

    对于每个 $G(x)$,我们可以用背包求。枚举 $x$ 的倍数也不怎么方便,所以我们可以将上下界以及 $m$ 都除掉 $x$,这样就可以使得 $a_i$ 连续,且减小背包容量,优化时间复杂度。

    对于每个 $G(x)$,背包容量可以降为 $lfloorfrac{m}{x} floor$。

    上文说了,$sumlimits_{i=1}^mfrac{m}{x}=mathcal{O}(m ln m)$。

    因此该做法总复杂度为 $mathcal{O}(nm ln m)$。

    说一下写代码时的注意点:

    • 前缀和数组的初始值为 $0$。
    • 注意方式背包时下标变负数的情况。
    • 做除法的时候,注意下界 $frac{l_i}{x}$ 要上取整。
    • 取模要取干净。

    这道题就这么做完了。具体细节可以看代码:

    #include <bits/stdc++.h>
    #define N 100010
    #define MOD 998244353
    #define reg register
    typedef long long ll;
    using namespace std;
     
    int n, m, l[N], r[N], dp[N], las[N], ss[N], ans, sss, slas[N];
     
    inline int F(int x, int y){        // 上取整
        return x / y + (x % y != 0);
    }
     
    int main(){
     
        cin >> n >> m;
        for(reg int i = 1; i <= n; i++) cin >> l[i] >> r[i];
        for(reg int d = m; d >= 1; d--){
            for(reg int j = 0; j <= m / d; j++)
                dp[j] = 0, las[j] = 0, slas[j] = 0;
            las[0] = slas[0] = 1;
            for(reg int j = 1; j <= m / d; j++)
                slas[j] = (slas[j - 1] + las[j]);
            for(reg int j = 1; j <= n; j++){
                for(reg int L = m / d; L >= F(l[j], d); L--){
                    if(L - r[j] / d > 0) dp[L] = (slas[L - F(l[j], d)] - slas[L - r[j] / d - 1] + MOD) % MOD;
                    else dp[L] = slas[L - F(l[j], d)];
                }
                for(reg int j = 0; j <= m / d; j++)
                    las[j] = dp[j], dp[j] = 0;
                slas[0] = las[0];
                for(reg int j = 1; j <= m / d; j++)
                    slas[j] = (slas[j - 1] + las[j]) % MOD;
            }
            for(reg int j = 0; j <= m / d; j++)
                ss[d] = (ss[d] + las[j]) % MOD;
            for(reg int j = d + d; j <= m; j += d)
                ss[d] = (ss[d] - ss[j] + MOD) % MOD;
        }
        printf("%d
    ", ss[1]);
     
        return 0;
    }
  • 相关阅读:
    laravel中使用include和component方法中的一点小区别
    【解决】Converting circular structure to JSON
    NUXT中使用axios,自己项目测试记录
    iPhone“连到系统上的设备没有发挥作用”原因分析及解决方法 20200105
    vue-touch不能上下滑动的问题【解决】
    动态加载swiper,默认显示最后一个swiper-slide怎么办
    CentOS 7 修改开机等待时间
    使用IIS管理器搭建FTP服务器以及其启用虚拟主机名
    织梦后台删除子栏目不成功的解决方法
    去除织梦标签[field:image/]所产生的宽高
  • 原文地址:https://www.cnblogs.com/zengpeichen/p/15161411.html
Copyright © 2020-2023  润新知