• 【CF585E】Present for Vitalik the Philatelist(因数容斥套路题)


    点此看题面

    • 给定一个大小为(n)的可重集。
    • 问有多少种方式从(n)中取出一个元素(x)和一个子集(S),满足(x otin S)(gcd{S}>1,gcd(gcd{S},x)=1)
    • (nle5 imes10^5),值域为([2,10^7])

    因数容斥

    应该算是一种做滥掉的套路了吧。。。

    这个套路大致就是,令(f(i))表示(d)(i)的倍数的方案数,(f'(i))表示(d=i)的方案数。

    显然有(f'(i)=f(i)-sum_{i|j}f'(j)),那么只要从大到小枚举(i)做一遍就好了,且这样子做一遍的复杂度应该是(O(Vln V))的。

    容斥+推式子

    首先,我们发现如果(gcd{S}=1),则(gcd(gcd{S},x)=1)必然成立。

    因此只需求出(gcd(gcd{S},x)=1)的方案数,然后减去(gcd{S}=1)的方案数,就可以得到题目中要求的方案数了。

    于是,我们设(f(i))表示(gcd(gcd{S},x))(i)的倍数的方案数,(g(i))表示(gcd{S})(i)的倍数的方案数,并套路地定义(f'(i))(g'(i))

    显然,答案就是(f'(1)-g'(1))。那么现在的问题就是如何快速计算(f(i))(g(i))

    考虑用(c(i))表示(i)的倍数个数。

    对于(f(i))

    其方案数就是在这(c(i))个数中枚举一个数作为(x),剩余的数任选(不能为空)作为(S)

    即:

    [c(i) imes(2^{c(i)-1}-1) ]

    对于(g(i))

    枚举(S)的大小(k),那么(x)就有(n-k)种取法,暴力计算式就是:

    [sum_{k=1}^{c(i)}C_{c(i)}^k imes(n-k) ]

    把括号拆开,式子就变成了两部分:

    [n imes sum_{k=1}^{c(i)}C_{c(i)}^k-sum_{k=1}^{c(i)}C_{c(i)}^k imes k ]

    对于前半部分:

    [n imes sum_{k=1}^{c(i)}C_{c(i)}^k=n imes(2^{c(i)}-1) ]

    对于后半部分:

    [egin{align} sum_{k=1}^{c(i)}C_{c(i)}^k imes k&=sum_{k=1}^{c(i)}frac{c(i)!}{(k-1)! imes (c(i)-k)!}\&=c(i) imessum_{k=1}^{c(i)}C_{c(i)-1}^{k-1}\&=c(i) imessum_{k=0}^{c(i)-1}C_{c(i)-1}^{k}\&=c(i) imes2^{c(i)-1} end{align} ]

    于是这道题就做完了。

    代码:(O(Vln V))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 500000
    #define V 10000000
    #define X 1000000007
    using namespace std;
    int n,a[N+5],pw[N+5],f[V+5],g[V+5],c[V+5],S[N+5];
    int main()
    {
    	RI i,j;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i),++c[a[i]];
    	for(i=1;i<=V;++i) for(j=i<<1;j<=V;j+=i) c[i]+=c[j];//求出每个数的倍数个数
    	for(pw[0]=i=1;i<=n;++i) pw[i]=(pw[i-1]<<1)%X;for(i=V;i;--i)
    	{
    		f[i]=1LL*c[i]*(pw[c[i]-1]-1)%X,g[i]=(1LL*n*(pw[c[i]]-1)-1LL*c[i]*pw[c[i]-1]%X+X)%X;//计算f和g
    		for(j=i<<1;j<=V;j+=i) f[i]=(f[i]-f[j]+X)%X,g[i]=(g[i]-g[j]+X)%X;//因数容斥
    	}return printf("%d
    ",(f[1]-g[1]+X)%X),0;//容斥计算答案
    }
    
  • 相关阅读:
    使用zipkin2在SpringCloud2.0环境下追踪服务调用情况
    Spring Cloud负载均衡:使用Feign作客户端负载均衡
    Spring Cloud负载均衡:使用zuul作服务器端负载均衡
    Word模板替换
    【转】Eureka集群
    巧用JavaScript语言特性解耦页面间调用(观察者模式)
    MySQL 视图触发器事务存储过程函数
    MySQL py模块的链接Navicat可视化工具
    MySQL 单表查询多表查询
    MySQL 表与表之间建立关系
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF585E.html
Copyright © 2020-2023  润新知