纪念第一次所有的解析全写在代码里面

QWQ

这里就简单说几句了

首先一个灯有贡献,当且仅当他被按了\(4k\)次。

那么我们定义\(f(S)\)表示\([1,n]\)中有多少个数\(x\)是集合\(S\)中元素的公倍数

\[f(S) = \frac{n}{lcm_{x\in S} x}
\]

这里需要注意的是,求\(lcm\)的时候,要两两合并,不能用整体的乘积除以\(gcd\)

但是很容易发现,要是这样计算的,会有重复的情况别包含进去,就比如说较小的集合公倍数,一定会包含它超集的公倍数,所以的话,我们定义

\(g(S)\)表示\([1,n]\)中有多少个数\(x\)是集合\(S\)的公倍数,且不存在更大的集合\(T\)使得\(x\)是\(T\)中元素的公倍数

可以通过容斥在\(O(3^m)\)内计算出来,大概就是对于一个集合\(S\),你去枚举他所有的超集,然后减去那些可能会重复的(原理和正解的类似,都写在代码里面了)

那么$$ans = \sum g(S) * \sum_k C_{length(s)}^{4k}\times 2^{m-length(s)}$$

这里的原理的,底下的代码里有写

QWQ

但是我们发现这个东西时间复杂度是跑不过,那么我们就需要一些其他角度的计算方式或者状态

QWQ由于我比较懒,直接搬dalao的博客了

另外我的很多想法都直接写在代码里面QWQ

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 1010;
const int mod = 998244353;
int c[maxn][maxn];
int n,m;
int a[maxn];
int ans;
int f[maxn],g[maxn];
void init()
{
for (int i=0;i<=1000;i++) c[i][i]=1,c[i][0]=1;
for (int i=2;i<=1000;i++)
{
for (int j=1;j<i;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
int gcd(int a,int b)
{
if (b==0) return a;
else return gcd(b,a%b);
}
ll qsm(ll i,ll j)
{
ll ans=1;
while (j)
{
if (j&1) ans=ans*i%mod;
i=i*i%mod;
j>>=1;
}
return ans;
}
signed main()
{
init();
n=read(),m=read();
for (int i=1;i<=m;i++) a[i]=read();
for (int i=0;i<(1 << m);i++)
{
int lcm = 1;
for (int j=1;j<=m;j++)
{
if ((1 << (j-1))&i)
{
lcm = lcm * a[j] / gcd(a[j],lcm); //两两lcm合并
if (lcm>n) break;
}
}
if (lcm>n) continue;
int ymh = __builtin_popcount(i);
//定义a[S]表示在[1,n]中,有多少个数是S集合的公倍数
f[ymh]+=n/lcm; //f[i]则表示所有长度为i的S的sigma(a[S])
f[ymh]%=mod;
}
//定义b(S)表示[1,n]中有多少个数x是集合S的公倍数,且不存在更大的集合T使得x是T中元素的公倍数;
//那么g(i)就表示对应长度i的集合的sigma(b[S])
for (int i=0;i<=m;i++) g[i]=f[i];
//因为考虑到一个长度的集合,我们可以合并到一起去算
//最后ans用g数组来算,就不会出现出现重复的情况了
for (int i=m;i>=0;i--)
for (int j=i+1;j<=m;j++)
g[i]=(g[i]-g[j]*c[j][i]%mod+mod)%mod; //这里可以理解为,就是每一个长度为j(j>i)的集合 ,都 为i的集合,而这些集合的公倍数,每一个都会在长度更小的集合中重复算一次,所以就减去QWQ了
//也就是说,对于长度为j的每一个b(S)中的数,都会在长度为i的他的子集中的对应的a(S)中出现,但是这个是不合法的,所以我们要减去这个贡献
for (int i=0;i<=m;i++) ans=(ans+qsm(2,m-i)*g[i]%mod*(c[i][0]+c[i][4]+c[i][8]+c[i][12]+c[i][16]+c[i][20])%mod)%mod;
//最后一行转移的式子是我们考虑枚举这个长度,然后只要选出来4k个,就一定是合法的(可以理解为g[i]中的数,在小的集合的贡献(这里子啊之前并不会算过,具体可以看g和b的定义),然后剩下的是随便选,因为我们考虑的是当前长度的贡献,
ans=ans%mod*qsm(qsm(2,m),mod-2)%mod;
cout<<ans;
return 0;
}

LOJ6356 四色灯(容斥+dp的更多相关文章

  1. [LOJ6356]四色灯

    [LOJ6356]四色灯 题目大意: 有\(n(n\le10^9)\)个编号\(1\sim n\)的格子和\(m(m\le20)\)个按钮.每个格子有一个初始为\(0\)的数,每个按钮有一个数字\(a ...

  2. HDU 5794 A Simple Chess (容斥+DP+Lucas)

    A Simple Chess 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5794 Description There is a n×m board ...

  3. [CF1086E]Beautiful Matrix(容斥+DP+树状数组)

    给一个n*n的矩阵,保证:(1)每行都是一个排列 (2)每行每个位置和上一行对应位置不同.求这个矩阵在所有合法矩阵中字典序排第几.考虑类似数位DP的做法,枚举第几行开始不卡限制,那么显然之前的行都和题 ...

  4. 【BZOJ3622】已经没有什么好害怕的了 容斥+DP

    [BZOJ3622]已经没有什么好害怕的了 Description Input Output Sample Input 4 2 5 35 15 45 40 20 10 30 Sample Output ...

  5. $bzoj2560$ 串珠子 容斥+$dp$

    正解:容斥+$dp$ 解题报告: 传送门$QwQ$ $umm$虽然题目蛮简练的了但还是有点难理解,,,我再抽象一点儿,就说有$n$个点,点$i$和点$j$之间有$a_{i,j}$条无向边可以连,问有多 ...

  6. 【XSY3156】简单计数II 容斥 DP

    题目大意 定义一个序列的权值为:把所有相邻的相同的数合并为一个集合后,所有集合的大小的乘积. 特别的,第一个数和最后一个数是相邻的. 现在你有 \(n\) 种数,第 \(i\) 种有 \(c_i\) ...

  7. bzoj3782上学路线(Lucas+CRT+容斥DP+组合计数)

    传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3782 有部分分的传送门:https://www.luogu.org/problemnew/ ...

  8. AGC 005D.~K Perm Counting(容斥 DP 二分图)

    题目链接 \(Description\) 给定\(n,k\),求 满足对于所有\(i\),\(|a_i-i|\neq k\)的排列的个数. \(2\leq n\leq 2000,\quad 1\leq ...

  9. ARC 101E.Ribbons on Tree(容斥 DP 树形背包)

    题目链接 \(Description\) 给定一棵\(n\)个点的树.将这\(n\)个点两两配对,并对每一对点的最短路径染色.求有多少种配对方案使得所有边都至少被染色一次. \(n\leq5000\) ...

随机推荐

  1. Python 脚本的执行

    源文件如下,文件名test.py,其中UTF-8根据实际情况而定,Python3默认为UTF-8,所以不用设置: #!/usr/bin/python # -*- coding: UTF-8 -*- p ...

  2. tcmalloc jemalloc glibc内存分配管理模块性能测试对比

    tcmalloc是谷歌提供的内存分配管理模块 jemalloc是FreeBSD提供的内存分配管理模块 glibc是Linux提供的内存分配管理模块 并发16个线程,分配压测3次,每次压15分钟,可以看 ...

  3. C++回调机制

    一直对回调机制不是很了解,今天索性搜了很多资料顺便整理一下,进步一点点. 1.Callback方式(回调函数) 什么是回调函数? 简而言之,回调函数就是一个通过函数指针调用的函数.如果你把函数的指针( ...

  4. 面试官:如何实现LRU?你学会了吗?

    面试官:来了,老弟,LRU缓存实现一下? 我:直接LinkedHashMap就好了. 面试官:不要用现有的实现,自己实现一个. 我:..... 面试官:回去等消息吧.... 大家好,我是程序员学长,今 ...

  5. vue 引用省市区三级联动(element-ui Cascader)

    npm 下载 npm install element-china-area-data -S main.js import {provinceAndCityData,regionData,provinc ...

  6. 如何在RHEL7或CentOS 7系统下修改网卡名称(亲测有效~!)

    亲测有效的更改RHEL7或CentOS 7的网卡名称的方法, 按照以下4步来操作就可以实现! Step 1 :网卡配置文件名称重命名为eth0[root@localhost ~]# ifconfige ...

  7. 我在组内的Nacos分享

    本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star. Nacos简介 Nacos : Naming and Configuration Servic ...

  8. python模块--glob, fnmatch

    包/方法 返回值 参数 说明 glob     Unix shell样式的路径扩展 .glob() list 匹配满足规则的所有路径(默认以 . 开头的文件不会匹配到, 可以用 .* 来匹配) pat ...

  9. MySQL MHA高可用集群部署及故障切换

    一.MHA概念MHA(MasterHigh Availability)是一套优秀的MySQL高可用环境下故障切换和主从复制的软件.MHA 的出现就是解决MySQL 单点的问题.MySQL故障切换过程中 ...

  10. javascript 中介者模式 mediator

    * player.js /** * 中介者模式 * @param {*} name 角色名称 * @param {*} teamColor 队伍颜色 */ function Player(name, ...