Codeforces 547C/548E - Mike and Foam 题解

前置芝士 - 容斥原理

容斥原理是简单的小学奥数求多个集合的并集的算法,最基本的思想大概是如下内容:

这是一道简单例题:有\(10\)个学生喜欢唱歌,有\(15\)个学生喜欢跳舞,有\(5\)个学生两种活动都喜欢,没有不喜欢前述两种活动的学生,那么一共有多少个学生呢?

相信聪明的你已经知道答案了,答案就是由如下算式计算得来:

\[10+15-5=20(个)
\]

对的,就是跟小学奥数一样。首先把喜欢唱歌或跳舞的学生都加起来,之后再减去重复计算的\(5\)个都喜欢的学生就可以了。

形式化地说(不过这里有三个集合,为了显示更多的规律):

\[\vert A \cup B \cup C \vert = \vert A \vert + \vert B \vert + \vert C \vert - \vert A \cap B \vert - \vert A \cap C \vert - \vert B \cap C \vert + \vert A \cap B \cap C \vert
\]

那么,如果有更多集合又该怎么算呢?其实也不难(表述可能不清楚qaq):

枚举所有集合组成的集合的子集(也就是01枚举每个集合),如果当前枚举的子集的个数是奇数,那么就加上它们交集的大小,否则(也就是偶数),就减去它们交集的大小。

形式化地说(这个应该清楚了):

\[\vert
\bigcup_{i=1}^{n}S_i
\vert
=
\sum_{T \subseteq U}
(-1)^{\vert T \vert - 1}
\vert
\bigcap_{E \in T}E
\vert
\]

\(S_i\)表示第\(i\)个要被求并集的集合,\(U\)表示把所有\(S_i\)作为元素的集合,\(T\)表示枚举出来的当前的\(U\)的子集,同样,\(T\)的每个元素都是一个集合,\(\vert T \vert\)表示\(T\)的包含的集合的个数,\(E\)表示如\(S_i\)一般包含普通的元素的集合。

题意

题意很简单,给你\(n\)杯啤酒,每个啤酒有数\(a_i\)和\(q\)个操作,每个查询包含一个数\(x\),表示对第\(x\)杯啤酒操作。如果这杯啤酒在架子上,就把它拿下来,否则,就放上去。之后输出当前放在架子上的啤酒里,上面的数互质的啤酒的对数,形式化地说,就是:

输出\((i,j)\)的对数,当它们满足\(i<j\)且\(\gcd(a_i,a_j)=1\),\(\gcd\)表示最大公因数。

以下把啤酒直接就叫做数字(它上面的那个标的数\(a_i\))了。

想法(口胡)

把每个数都拆成质因子序列,并把质因子当成是要求并集的集合,然后用容斥和一些数据结构就可以快速计算与当前数互质的数的个数。

做法

首先,我们把每个数都拆成它们的质因子相乘的序列,有相同的质因子就只保留其中的一个。然后,我们再维护一个数据结构(推荐std::map,方便好用),保存的是枚举出的质因子相乘得到的某一个它的因数(第一关键字),以它作为因子(除以它余\(0\))的数\(a_i\)的个数(第二关键字)。之后用容斥的方法,就可以算出每一个数当前不与它互质的在架子上的数的个数,用总数减一下就是互质的数的个数了。添加和删去数也很方便:添加时就把所有它的因子(特指用枚举质因子相乘得到的)在map里的值加上一,删除时就减去一就好了。

程序

感觉前面做法就是描述的程序写法啊(果然还是我的表述太不清楚了吧)

#include<bits/stdc++.h>
using namespace std; typedef long long ll; int n,q,cnt;//cnt表示在架子上的啤酒的个数
ll tot;//tot是当前的所有的互质的数对的数量
int a[200005];
map<int,int> mp;//保存包含某个数作为因数的数a[i]的个数
vector<int> p[200005];//把数拆成质因子序列
bool onshelf[200005];//是否在架子上 void ext(int x,vector<int> &ps){
if(x==1){//特判一就不拆了
return;
}
for(int i=2;i*i<=x;i++){
if(x%i==0){
ps.push_back(i);
while(x%i==0)x/=i;//去除重复的因子
}
}
if(x>1)ps.push_back(x);//循环完可能还会有一个因子没有分解
} void puton(int x){//添加第x个数到架子
int res=cnt;//假设所有数都互质
for(int s=1;s<(1<<p[x].size());s++){//枚举当前使用的质因数的集合
int mt=1,bts=0;//mt为乘积,bts记录子集大小
for(int i=0;i<p[x].size();i++){
if(s&(1<<i)){
mt*=p[x][i];
bts++;
}
}//算出乘积
if(bts&1){
res-=mp[mt];
}else{
res+=mp[mt];
}//容斥,正负相反是因为前面假设都是互质的
mp[mt]++;//添加这个因子,由于枚举出的mt各不相同,所以不会重复计算
}
tot+=res;
} void takeoff(int x){//拿走,基本同上
int res=cnt;
for(int s=1;s<(1<<p[x].size());s++){
int mt=1,bts=0;
for(int i=0;i<p[x].size();i++){
if(s&(1<<i)){
mt*=p[x][i];
bts++;
}
}
mp[mt]--;//先减去自己的那个因子,防止重复
if(bts&1){
res-=mp[mt];
}else{
res+=mp[mt];
}
}
tot-=res;
} int main(){ ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0); cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
ext(a[i],p[i]);
}
while(q--){
int x;cin>>x;
if(onshelf[x]){
cnt--;
takeoff(x);
}else{
puton(x);
cnt++;
}
onshelf[x]^=1;//作用就是取反
cout<<tot<<endl;
} return 0;
}

感谢

感谢你看完了!我真的是很不擅长数论qaq,如果有什么写得不清楚的欢迎在下面留言~

Codeforces 547C/548E - Mike and Foam 题解的更多相关文章

  1. Codeforces 548E Mike ans Foam (与质数相关的容斥多半会用到莫比乌斯函数)

    题面 链接:CF548E Description Mike is a bartender at Rico's bar. At Rico's, they put beer glasses in a sp ...

  2. codeforces #305 C Mike and Foam

    首先我们注意到ai<=50w 因为2*3*5*7*11*13*17=510510 所以其最多含有6个质因子 我们将每个数的贡献分离, 添加就等于加上了跟这个数相关的互素对 删除就等于减去了跟这个 ...

  3. hdu4135-Co-prime & Codeforces 547C Mike and Foam (容斥原理)

    hdu4135 求[L,R]范围内与N互质的数的个数. 分别求[1,L]和[1,R]和n互质的个数,求差. 利用容斥原理求解. 二进制枚举每一种质数的组合,奇加偶减. #include <bit ...

  4. E. Mike and Foam(容斥原理)

    E. Mike and Foam Mike is a bartender at Rico's bar. At Rico's, they put beer glasses in a special sh ...

  5. cf#305 Mike and Foam(容斥)

    C. Mike and Foam time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...

  6. # Codeforces Round #529(Div.3)个人题解

    Codeforces Round #529(Div.3)个人题解 前言: 闲来无事补了前天的cf,想着最近刷题有点点怠惰,就直接一场cf一场cf的刷算了,以后的题解也都会以每场的形式写出来 A. Re ...

  7. Codeforces Round #557 (Div. 1) 简要题解

    Codeforces Round #557 (Div. 1) 简要题解 codeforces A. Hide and Seek 枚举起始位置\(a\),如果\(a\)未在序列中出现,则对答案有\(2\ ...

  8. Codeforces Round #665 (Div. 2)A-C题解

    A. Distance and Axis 题目:http://codeforces.com/contest/1401/problem/A 题解:对于n来说分两种情况,一是奇数,二则是偶数 ①奇数:对于 ...

  9. Codeforces Round #668 (Div. 2)A-C题解

    A. Permutation Forgery 题目:http://codeforces.com/contest/1405/problem/A 题解:这道题初看有点吓人,一开始居然想到要用全排序,没错我 ...

随机推荐

  1. HDU-3727 Jewel

    Jimmy wants to make a special necklace for his girlfriend. He bought many beads with various sizes, ...

  2. 记录一些实用的小技巧-CSS篇

    1.单行文本截断 .text{ width: 200px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } 2.多行 ...

  3. 【CuteJavaScript】Angular6入门项目(4.改造组件和添加HTTP服务)

    本文目录 一.项目起步 二.编写路由组件 三.编写页面组件 1.编写单一组件 2.模拟数据 3.编写主从组件 四.编写服务 1.为什么需要服务 2.编写服务 五.引入RxJS 1.关于RxJS 2.引 ...

  4. 【Selenium】自动进入网页,出现弹窗被卡住

    问题现象: 使用命令:driver.get("http://127.0.0.1/zentao/user-login.html") 进入网页,出现如下弹窗,无法进入 解决方法: #d ...

  5. 【搞定Jvm面试】 JVM 垃圾回收揭秘附常见面试题解析

    JVM 垃圾回收 写在前面 本节常见面试题 问题答案在文中都有提到 如何判断对象是否死亡(两种方法). 简单的介绍一下强引用.软引用.弱引用.虚引用(虚引用与软引用和弱引用的区别.使用软引用能带来的好 ...

  6. Nginx(一)--nginx的初步认识及配置

    什么是Nginx 是一个高性能的反向代理服务器正向代理代理的是客户端反向代理代理的是服务端 Apache.Tomcat.Nginx 静态web服务器jsp/servlet服务器 tomcat 安装Ng ...

  7. Kubernetes 应用部署实战

    Kubernetes 应用部署实战 2018-08-08 19:44:56 wuxiangping2017 阅读数 3084  收藏 更多 分类专栏: linux运维与架构师   简介 伙计们,请搬好 ...

  8. 一线大厂面试官最喜欢问的15道Java多线程面试题

    前言 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分.如果你想获得更多职位,那么你应该准备很多关于多线程的问题. 他们会问面试者很多令人混淆的Java线程问题.面试官只是想确信面试者 ...

  9. 手动SQL注入原理分析与实践

    代码仓库 本文所用代码的代码库地址: 点击这里前往Github仓库 了解SQL注入 定义 SQL注入攻击(SQL Injection),简称注入攻击,是Web开发中最常见的一种安全漏洞.可以用它来从数 ...

  10. Python批量更新模块的方法【面试必学】

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:ranchlin      小编的环境为win10+python 3. ...