zoukankan      html  css  js  c++  java
  • LOJ6356 四色灯

    传送门

    子任务1:m=1

    直接算两遍选或不选就可以了

    期望得分:10

    子任务2:n<=1e4 m<=5

    两种方法

    一。枚举每一个数,看它被几个数整除,它对答案有贡献当且仅当被选4k次(k∈N)所以一个数的贡献就是sum inom{x}{4k}*2^{m-x}

    时间复杂度:O(nm)

    二。枚举m的子集,因为m<=5可以进行类似求n个数里不被某几个数整除的数的个数一样的容斥,只需要把4的容斥系数改成2就可以了。时间复杂度:O(3^m*m)

    期望得分:30

    子任务3:n<=1e9 m<=20

    首先我们根据子任务二中枚举m的子集的方法拓展一下。

    令f(s)表示[1,n]中多少数是s集合中的数的公倍数

    f(s)=frac{n}{lcm_{x epsilon s} x}

    g(s)表示[1,n]中恰好被s整除但不会被除s的子集外整除的数的个数

    g(s)=g(x)*(-1)^{|s|-|x|} ({x epsilon s})可以通过枚举子集来进行转移 时间复杂度O(3^n)

    我们接下来开始优化

    令F(i)表示sum_{|s|=i} f(s) G(i)同理

    G(i)=F(i)-sum_{j=i+1}^m inom{j}{i}G(j)这个可以理解成因为对于j>i所以j一定包含了C(j,i)个大小为i的子集所以我们把它减掉就可以了

    G(i)=sum _{j=i}^{n} (-1)^{j-i}inom{j}{i}F(j)这个其实就是把上面的G(j)改成以F的容斥所以长的差不多qwq

    很明显F可以在O(2^m*m)的时间复杂度内计算完

    G可以在O(m^2)的时间复杂度容斥

    所以总复杂度为O(2^m*m)

    期望得分100

    附代码。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define inf 20021225
    #define ll long long
    #define mdn 998244353
    #define mod(x) (x>=mdn?x-mdn:x)
    using namespace std;
    
    int m,a[23],n;
    int G[23],f[23][23];
    int gcd(int x,int y)
    {
    	return y==0?x:gcd(y,x%y);
    }
    int count(int x)
    {
    	int cnt=0;
    	while(x)
    	{
    		if(x&1)	cnt++;
    		x>>=1;
    	}
    	return cnt;
    }
    void pre()
    {
    	f[0][0]=f[1][0]=f[1][1]=1;
    	for(int i=2;i<=m;i++)
    	{
    		f[i][i]=f[i][0]=1;
    		for(int j=1;j<i;j++)
    			f[i][j]=mod(f[i-1][j]+f[i-1][j-1]);
    	}
    }
    int C(int x,int y)
    {
    	return f[x][y];
    }
    int ksm(int bs,int mi)
    {
    	int ans=1;
    	while(mi)
    	{
    		if(mi&1)	ans=(ll)ans*bs%mdn;
    		bs=(ll)bs*bs%mdn;mi>>=1;
    	}
    	return ans;
    }
    int main()
    {
    	int ans=0;
    	scanf("%d%d",&n,&m);pre();
    	for(int i=1;i<=m;i++)	scanf("%d",&a[i]);
    	for(int i=1;i<(1<<m);i++)
    	{
    		int g=1,f,j,cnt=count(i);
    		for(j=0;j<m;j++)
    		{
    			if(i&(1<<j))
    			{
    				f=gcd(a[j+1],g);
    				if((ll)g/f*a[j+1]>n)	break;
    				g=g/f*a[j+1];
    			}
    		}
    		if(j<m)	continue;
    		else	G[cnt]=mod(G[cnt]+n/g);
    	}
    	G[0]=n;
    	for(int i=m;~i;i--)
    		for(int j=i+1;j<=m;j++)
    			G[i]=mod(G[i]-(ll)G[j]*C(j,i)%mdn+mdn);
    	//for(int i=0;i<=m;i++)	printf("%d
    ",G[i]);
    	for(int i=0;i<=m;i++)
    		for(int j=0;j<=i;j+=4)
    			ans=mod(ans+(ll)G[i]*C(i,j)%mdn*(1<<m-i)%mdn);
    	printf("%d
    ",(ll)ans*ksm((1<<m),mdn-2)%mdn);
    	return 0;
    }
    
  • 相关阅读:
    4.练习
    『Java面试题总结
    『Maven + Junit + Jacoco』单元测试覆盖率
    『Linux』命令
    『ElasticSearch』安装、健康值检查
    『类型转换』Object转Map、Map转Object
    『ElasticSearch』排序报错
    MD5加密解密网址总结
    汉字编码的理解
    ASCII码的理解
  • 原文地址:https://www.cnblogs.com/hanyuweining/p/10321949.html
Copyright © 2011-2022 走看看