'commit'
This commit is contained in:
18
TangDou/AcWing_TiGao/T5/RongChi/199.cpp
Normal file
18
TangDou/AcWing_TiGao/T5/RongChi/199.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
|
||||
int n, k, l, r;
|
||||
int ans;
|
||||
signed main() {
|
||||
cin >> n >> k;
|
||||
ans = n * k; // 看题解的推导公式
|
||||
for (l = 1; l <= n; l = r + 1) { // 枚举左端点,每次跳着走,下次的位置就是本次r的位置+1
|
||||
if (k / l == 0) break; // 1、当k/l=0的时候,比如3/4=0,3/5=0,3/6=0...,以后就都是0,不用再往后算了,无贡献
|
||||
r = min(k / (k / l), n); // 2、注意右端点和n取个min,>n没有贡献
|
||||
ans -= (k / l) * (l + r) * (r - l + 1) / 2;
|
||||
// 等差数列求和:首项:l,末项:r.项数:(r-l+1),根据公式推导,k/l = 块内值
|
||||
}
|
||||
cout << ans << endl;
|
||||
}
|
||||
134
TangDou/AcWing_TiGao/T5/RongChi/199.md
Normal file
134
TangDou/AcWing_TiGao/T5/RongChi/199.md
Normal file
@@ -0,0 +1,134 @@
|
||||
##[$AcWing$ $199$. 余数之和](https://www.acwing.com/problem/content/description/201/)
|
||||
|
||||
### 一、题目描述
|
||||
给出正整数 $n$ 和 $k$,计算 $j(n,k)=k~mod~1+k~mod~2+k~mod~3+…+k~mod~n$ 的值。
|
||||
|
||||
例如 $j(5,3)=3~mod~1+3~mod~2+3~mod~3+3~mod~4+3~mod~5=0+1+0+3+3=7$。
|
||||
|
||||
**输入格式**
|
||||
输入仅一行,包含两个整数 $n,k$。
|
||||
|
||||
**输出格式**
|
||||
输出仅一行,即 $j(n,k)$。
|
||||
|
||||
**数据范围**
|
||||
$1≤n,k≤10^9$
|
||||
|
||||
**输入样例:**
|
||||
```cpp {.line-numbers}
|
||||
5 3
|
||||
```
|
||||
**输出样例:**
|
||||
```cpp {.line-numbers}
|
||||
7
|
||||
```
|
||||
### 二、暴力作法
|
||||
> **注**:暴力作法用于骗分+理解题意
|
||||
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
|
||||
int n, k, ans;
|
||||
signed main() {
|
||||
// 通过了 6/10个数据
|
||||
cin >> n >> k;
|
||||
for (int i = 1; i <= n; i++) ans += k % i;
|
||||
cout << ans << endl;
|
||||
}
|
||||
```
|
||||
|
||||
### 三、整除分块
|
||||
|
||||
我们先研究一下如下的序列有哪些特点,再结合本题实际给出解题思路:
|
||||
|
||||
$$\large \displaystyle f(i)=\lfloor \frac{k}{i} \rfloor ~ (i \in [1,n])$$
|
||||
|
||||
> **注**:本题要求的是$[1 \sim n]$之内所有数字对数字$k$ **取模**,我们不直接研究取模,而是在研究$[1 \sim n]$之内每个数字被$k$整除的结果。这个结果,与 **取模** 是有关系的,后面的题解中也会证明这一点。
|
||||
|
||||
举个例子,$n = 4,k=5$,
|
||||
$f(1)=k/1=5/1=5 \\
|
||||
f(2)=k/2=5/2=2 \\
|
||||
f(3)=k/3=5/3=1,f(4)=k/4=5/4=1,f(5)=k/5=5/5=1$
|
||||
|
||||
一字排开,得到$5,2,1,1,1$,相同的分到一个 **连续一样的数字段** 中,直观一点,写成:$[5],[2],[1,1,1]$
|
||||
|
||||
<center><img src='https://cdn.acwing.com/media/article/image/2022/03/25/94631_bd6771ecab-4ocKnIkdlrW9JMs.png'></center>
|
||||
|
||||
|
||||
### 四、本题题解
|
||||
**题意**:给出$n,k$,求 $\displaystyle \sum_{i=1}^{n}k \ mod \ i$。
|
||||
|
||||
首先取模形式不好处理,根据取模运算定义做 **等价变形**:
|
||||
|
||||
$$\large \sum_{i=1}^nk \ mod \ i=\sum_{i=1}^n(k-⌊\frac{k}{i}⌋\cdot i)$$
|
||||
|
||||
化简
|
||||
|
||||
$$\large n \cdot k-\sum_{i=1}^n ⌊\frac{k}{i}⌋\cdot i$$
|
||||
|
||||
我们发现重点在于求$\large \displaystyle \sum_{i=1}^n ⌊\frac{k}{i}⌋ \cdot i$。
|
||||
|
||||
发现$\large \underset{\text{i=1}}{\overset{\text{n}}{⌊\frac{k}{i}⌋}}$ 的值 **呈块状分布**(即结果数组分成若干块,每块中值相等),是 **整除分块**。
|
||||
|
||||
|
||||
|
||||
#### 1、 块内的累加和是多少?
|
||||
首先 **一个块内部** 的答案显然是好求的,设块起点为 $l$,终点为 $r$,同时$\displaystyle \large ⌊\frac{k}{l}⌋=⌊\frac{k}{l+1}⌋=...=⌊\frac{k}{r}⌋$,
|
||||
累加和是$\large \displaystyle ⌊\frac{k}{l}⌋ \cdot \sum_{i=l}^r i$。
|
||||
|
||||
#### 2、如何根据一个块的起点求块的终点呢?
|
||||
|
||||
① 下一个块的起点就是前一个块的终点加$1$。
|
||||
② 第一个块的起点位置坐标肯定是$1$
|
||||
> **注**:这里说的第一个块的起点,是指最终结果数组中块的下标位置,如上例,就是
|
||||
> 序列 ` 5 2 1 1 1`
|
||||
> **序号** ` 1 2 3 4 5`
|
||||
|
||||
③ 如果有办法能通过一个块的起点,找到这个块的终点,那就能确定一个块的范围,我们来研究一下这个办法:
|
||||
|
||||
首先由于 **块内值** 都相同,可以设 $\large \displaystyle x= ⌊\frac{k}{l}⌋= ⌊\frac{k}{r}⌋$
|
||||
|
||||
|
||||
根据 $\displaystyle \large x=⌊\frac{k}{r}⌋$ ① $\Rightarrow$ 变形 $\Rightarrow $ $\large \displaystyle x \cdot r \leq k$ $\Rightarrow$ 变形 $\Rightarrow$ $\large \displaystyle r \leq ⌊\frac{k}{x}⌋$ ②
|
||||
|
||||
将$\large \displaystyle x=⌊\frac{k}{l}⌋$ ① 代入 ②,得$\large \displaystyle r \leq ⌊\frac{k}{⌊\frac{k}{l}⌋}⌋$,这样就确定了$r$的最大值。
|
||||
|
||||
> **注**:当$k,l$确定时,$r$的上限就已经确定,同时,由于第一起点块的$l=1$,我们就可以一路向后递推找出所有块的右边界!一旦有了右边界,就可以利用 $\large \displaystyle ⌊\frac{k}{l}⌋\cdot \sum_{i=l}^ri$来快速计算出区间和。
|
||||
|
||||
|
||||
#### 3、时间复杂度【选读】
|
||||
|
||||
当 $i>\sqrt{n}$ 时,$\large \displaystyle ⌊\frac{k}{i}⌋ \leq \sqrt{n}$,也就是说原式只有小于 $\sqrt{k}$ 种取值。
|
||||
> **注**:纯粹的数学思考,除以一个$ \geq \sqrt{n}$的值,$n,k$是同一个数据级别$\leq 10^9$,结果值当然是$\leq \sqrt{n}$
|
||||
|
||||
当$i \leq \sqrt{n}$时,$i$只有小于$\sqrt{k}$种取值,也就是说原式也只有小于$\sqrt{k}$种取值。
|
||||
|
||||
|
||||
所以最多有 $2\sqrt{n}$ 个块,我们对于每个块可以 $O(1)$ 计算,时间可以通过。
|
||||
|
||||
|
||||
### $Code$
|
||||
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
|
||||
int n, k, l, r;
|
||||
int ans;
|
||||
signed main() {
|
||||
cin >> n >> k;
|
||||
ans = n * k; // 看题解的推导公式
|
||||
for (l = 1; l <= n; l = r + 1) { // 枚举左端点,每次跳着走,下次的位置就是本次r的位置+1
|
||||
if (k / l == 0) break; // 1、当k/l=0的时候,比如3/4=0,3/5=0,3/6=0...,以后就都是0,不用再往后算了,无贡献
|
||||
r = min(k / (k / l), n); // 2、注意右端点和n取个min,>n没有贡献
|
||||
ans -= (k / l) * (l + r) * (r - l + 1) / 2;
|
||||
// 等差数列求和:首项:l,末项:r.项数:(r-l+1),根据公式推导,k/l = 块内值
|
||||
}
|
||||
cout << ans << endl;
|
||||
}
|
||||
```
|
||||
12
TangDou/AcWing_TiGao/T5/RongChi/199_BruteForce.cpp
Normal file
12
TangDou/AcWing_TiGao/T5/RongChi/199_BruteForce.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
|
||||
int n, k, ans;
|
||||
signed main() {
|
||||
// 通过了 6/10个数据
|
||||
cin >> n >> k;
|
||||
for (int i = 1; i <= n; i++) ans += k % i;
|
||||
cout << ans << endl;
|
||||
}
|
||||
BIN
TangDou/AcWing_TiGao/T5/RongChi/214.eddx
Normal file
BIN
TangDou/AcWing_TiGao/T5/RongChi/214.eddx
Normal file
Binary file not shown.
261
TangDou/AcWing_TiGao/T5/RongChi/214.md
Normal file
261
TangDou/AcWing_TiGao/T5/RongChi/214.md
Normal file
@@ -0,0 +1,261 @@
|
||||
##[$AcWing$ $214$. $Devu$和鲜花 ](https://www.acwing.com/problem/content/description/216/)
|
||||
|
||||
### 一、题目描述
|
||||
$Devu$ 有 $N$ 个盒子,第 $i$ 个盒子中有 $A_i$ 枝花。
|
||||
同一个盒子内的花颜色相同,不同盒子内的花颜色不同。
|
||||
$Devu$ 要从这些盒子中选出 $M$ 枝花组成一束,**求共有多少种方案**。
|
||||
|
||||
**若两束花每种颜色的花的数量都相同,则认为这两束花是相同的方案。**
|
||||
|
||||
结果需对 $10^9+7$ 取模之后方可输出。
|
||||
|
||||
**输入格式**
|
||||
第一行包含两个整数 $N$ 和 $M$。
|
||||
|
||||
第二行包含 $N$ 个空格隔开的整数,表示 $A_1,A_2,…,A_N$。
|
||||
|
||||
**输出格式**
|
||||
输出一个整数,表示方案数量对 $10^9+7$ 取模后的结果。
|
||||
|
||||
**数据范围**
|
||||
$1≤N≤20,0≤M≤10^{14},0≤A_i≤10^{12}$
|
||||
|
||||
**输入样例:**
|
||||
```cpp {.line-numbers}
|
||||
3 5
|
||||
1 3 2
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```cpp {.line-numbers}
|
||||
3
|
||||
```
|
||||
|
||||
### 二、隔板法复习
|
||||
|
||||
#### 1、隔板基本法【每组小球数至少为$1$】
|
||||
$Q$:有 `m` 个相同的小球,要求分到 `n` 个盒子里,<font color='red'><b>每组至少小球数为$1$</b></font>,问有多少种不同的分法?
|
||||
|
||||
$A$:从`m-1`个空隙中找出`n-1`个位置放上隔板,就可以保证最终分成`n`组,并且,每一组的数量都最少是`1`个或以上。
|
||||
<center><img src='https://img-blog.csdnimg.cn/20190423123438869.png'></center>
|
||||
|
||||
其实,有多少种划分方法,就是有多少种不同颜色组合的方案,比如:
|
||||
<center><img src='https://img-blog.csdnimg.cn/20190423123537228.png'></center>
|
||||
|
||||
如上图,把隔板隔开的不同小球,涂上不同颜色,不就是不同颜色的组合方案了嘛~
|
||||
|
||||
|
||||
**隔板法的思路**:找空隙(**共$m-1$个空隙**),插入隔板(**$n-1$个隔板,这样才能分成$n$组**),这类问题比较直观:$$\LARGE C_{m-1}^{n-1} $$
|
||||
|
||||
#### 2、隔板扩展法【每组小球数可以为$0$】
|
||||
有时,有的问题不能直接使用隔板法,比如:
|
||||
有 `m` 个相同的元素,要求分到 `n` 组中,<font color='red'><b>每组个数可以为$0$</b></font>,问有多少种不同的分法?
|
||||
|
||||
这个问题对比上面的 **隔板法**,区别在于没有强调 **每组至少元素数为$1$** ,每组是可以为`0`的,这样一来,无法直接使用隔板法。
|
||||
|
||||
采用一个 **变形**,就可以使用隔板法:
|
||||
|
||||
原来有`m`个小球,我再多拿`n`个小球,就是一共`m+n`个小球。这些小球,每组先来一个,就保证了每组 **至少** 小球数为$1$,就 **转化为隔板法的基本问题** 了。
|
||||
|
||||
现在是`n+m`个小球,空隙是 `n+m-1`个。
|
||||
|
||||

|
||||
|
||||
由于还是要分成`n`组,所以现在需要在`n+m-1`个空隙中找到`n-1`个位置放入隔板,分法就是$\large \displaystyle C_{m+n-1}^{n-1}$,最后,把每个分组中再取走增加进去的那个小球,这样就和原问题一致了。
|
||||
|
||||
### 三、本题思路
|
||||
|
||||
**[容斥原理模板题 $AcWing$ $890$. 能被整除的数 ](https://www.cnblogs.com/littlehb/p/15389237.html)**
|
||||
|
||||
|
||||
#### 1、转化为 隔板扩展法
|
||||
先不考虑每个盒子的个数$A_i$限制,**简化问题** 为:从$n$个盒子中每个都任意选出$x_i$个,每个$x_i$**可以为$0$**,可以使用 **隔板扩展法**,答案:
|
||||
$$\large \displaystyle C_{m + n − 1}^{n-1}$$
|
||||
|
||||
> **解读**:盒子数量 $n$,需要$n-1$个隔板。但要求每个盒子中小球数量可以为$0$,所以,先借$n$个小球,最后归还即可。此时,共$m+n$个小球,有 $m+n-1$个空隙,因为需要划分为$n$个盒子,所以,需要找出$n-1$个空隙,即$C_{m+n-1}^{n-1}$
|
||||
|
||||
#### 2、每组个数限制$a_i$
|
||||
但题目并没有那么简单,每个箱子中选择的个数$x_i$,不是随意多少都行的,需要小于 **现存个数** $a_i$。
|
||||
|
||||
<font color='red'><b>这个限制不好加上去~</b></font> ,正着想困难,我们倒着想试试:
|
||||
|
||||
<font color='blue' size=4><b>问题转化为补集:所有方案数-不满足至少一种条件的方案数</b></font>
|
||||
|
||||
|
||||
**(1)、如果我们把不满足条件的去掉,是不是就行了呢?**
|
||||
|
||||
什么是不满足条件呢?答:如果某个$x_i>a_i$,就是不满足! 即$x_i>=a_i+1$
|
||||
|
||||
<font color='red'><b>令$S_i$代表第$i$组不满足的方案个数,即在第$i$组中至少取走$a_i+1$个的方案个数。</b></font>
|
||||
|
||||
**(2)、把这样不满足的都减掉是不是就是答案呢?**
|
||||
|
||||
**答**:<font color='red'><b>不是!</b></font>
|
||||
|
||||
比如要求第$1$组中不能多于$2$个,第$2$组中不能多于$3$个,现在有一组答案$(3,4,2,..)$,表示第一组选择了$3$个,第二组中选择了$4$个,很明显这个答案对于两个限制都是不能满足的:
|
||||
|
||||
- ① 在检查到第一组时,此答案被去掉一次
|
||||
- ② 在检查到第二组时,它又被去掉了一次
|
||||
|
||||
但问题是这只是一组数据,现在被去了两次啊!噢,这是 <font color='red'><b>容斥原理</b></font> 的问题啊~,还得把减两次的加回来一次才对!
|
||||
|
||||
$$\large C_{m+n-1}^{n-1}-|S_1|-|S_2|-...-|S_n|+|S_1\cap S_2|+|S_1 \cap S_3|+...$$
|
||||
|
||||
> **解读**:模板题中奇数的加,偶数的减,这里怎么是奇数的减,偶数的加呢?
|
||||
> 思考一下知道,这应该是题目本来就是要 **去掉不合法** 的情况,去掉嘛,就是减
|
||||
> **小结**:受一个条件限制的,需要减去,受两个条件限制的由于已经被单个条件减去了两次,所以需要加上两者的交集,以此类推,就是 **奇数次出现的减,偶数次出现的加**。
|
||||
> **总结**:容斥原理可不是一定要奇数加,偶数减,需要具体问题具体分析。究其原因,就是因为前面可能有负号,脱括号后后面的符号就会反转!
|
||||
|
||||
**(3)、$S_i$怎么求?**
|
||||
|
||||
比如求$S_1$,代表从第一组里 <font color='red' size=4><b>至少取出$A_1+1$朵花</b></font>,此时还剩$m−(A_1+1)$朵花。
|
||||
|
||||
则问题转化为选$m−(A_1+1)$只花分$n$组的问题。
|
||||
|
||||
> **解读**:
|
||||
> 至少取出$A_1+1$只花,那就先拿出来放一边。然后考虑剩下的$m-(A_1+1)$只花怎么分的问题。
|
||||
> 把这些花直接分$n$组,分组数量可以为$0$,划分完后,再把刚才拿走的那些花还给第$1$组就行了。
|
||||
在$m-(A_1+1)$个小球需要划分$n$组,并且每组个数可以为$0$的方案数:
|
||||
>$$\large |S_1|=C_{m-(A_1+1)+n-1}^{n-1}$$
|
||||
|
||||
**(4)、$|S_1 \cap S_2|$怎么求?**
|
||||
$|S_1 \cap S_2|$表示从第一组里取出至少$A_1+1$朵花,并且,从第二组里取出至少$A_2+1$朵花。方案数
|
||||
$$\large |S_1 \cap S_2|=C_{m-(A_1+1)-(A_2+1)+n-1}^{n-1}$$
|
||||
|
||||
> **解读**:参考上面的解释内容,先把$A_1+1,A_2+1$拿出来,然后把剩下的花$m-(A_1+1)-(A_2+1)$只花继续划分为$n$组,每组允许为$0$只花,**隔板扩展法**,最后再把$A_1+1,A_2+1$放到第$1,2$组中去。
|
||||
|
||||
**(5)、计算公式**
|
||||
整理一下,得到:
|
||||
|
||||
$$\large res=C_{m+n-1}^{n-1}-\sum_{i=1}^{n}C_{m+n-1-(A_i+1)}^{n-1}+\sum_{i<j}^{n}C_{m+n-1-(A_i+1)-(A_j+1)}^ {n-1}-...$$
|
||||
|
||||
**(6)、为什么可以用费马小定理求逆元?**
|
||||
$Q$:$(n−1)!$一定不是质数$p$的倍数吗?为什么呢?
|
||||
**答**:$n$最大是$20$,$p$是$1e9 + 7$,考虑$n$是从$1$到$20$的乘积,每一个乘数都$p$互质,所以$(n - 1)!$一定与$p$互质,所以一定不是倍数。
|
||||
|
||||
**(7)、代码实现**
|
||||
从$1$枚举到$2^{n-1}$。然后把 **每一个限制条件** 看成一个二进制位,如果是$1$代表 **遵守** ,$0$代表 **不遵守** 这个条件,奇数个就减,偶数个就加。
|
||||
|
||||
> **解读**:
|
||||
> $0000$代表所有限制条件都不遵守,也就是初始值$C_{n+m-1}^{n-1}$
|
||||
> $0001$代表第$1$个约束条件遵守,其它约束条件不遵守
|
||||
> ...
|
||||
> $1111$代表所有限制条件都遵守
|
||||
|
||||
怎么去算组合数,可以发现虽然$M$非常大,但是$N$很小,所以只需按着定义去算,大概是$O(N)$的复杂度。
|
||||
|
||||
总的复杂度:$O(2^N∗N)$
|
||||
|
||||
#### $Code$
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
const int N = 20, mod = 1e9 + 7;
|
||||
|
||||
int A[N];
|
||||
int n, m;
|
||||
|
||||
// 快速幂
|
||||
int qmi(int a, int k) {
|
||||
int res = 1;
|
||||
while (k) {
|
||||
if (k & 1) res = res * a % mod;
|
||||
a = a * a % mod;
|
||||
k >>= 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int C(int a, int b) {
|
||||
if (a < b) return 0;
|
||||
int up = 1, down = 1;
|
||||
for (int i = a; i > a - b; i--) up = i % mod * up % mod;
|
||||
for (int i = 1; i <= n - 1; i++) down = i * down % mod; //(n-1)! % mod
|
||||
down = qmi(down, mod - 2); // 费马小定理求逆元
|
||||
|
||||
return up * down % mod; // 费马小定理
|
||||
}
|
||||
|
||||
signed main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 0; i < n; i++) cin >> A[i];
|
||||
|
||||
int res = C(n + m - 1, n - 1);
|
||||
for (int i = 1; i < (1 << n); i++) {
|
||||
int sum = 0, cnt = 0;
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (i >> j & 1) {
|
||||
sum += A[j] + 1;
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt & 1)
|
||||
res = (res - C(m + n - 1 - sum, n - 1) + mod) % mod;
|
||||
else
|
||||
res = (res + C(m + n - 1 - sum, n - 1)) % mod;
|
||||
}
|
||||
cout << res << endl;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
附上最开始我没有看懂的$yxc$大佬代码,让我们一起批判他吧:
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
const int N = 20, mod = 1e9 + 7;
|
||||
|
||||
int A[N];
|
||||
int n, m;
|
||||
|
||||
// 快速幂
|
||||
int qmi(int a, int k) {
|
||||
int res = 1;
|
||||
while (k) {
|
||||
if (k & 1) res = res * a % mod;
|
||||
a = a * a % mod;
|
||||
k >>= 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int C(int a, int b) {
|
||||
if (a < b) return 0;
|
||||
int up = 1, down = 1;
|
||||
for (int i = a; i > a - b; i--) up = i % mod * up % mod;
|
||||
for (int i = 1; i <= n - 1; i++) down = i * down % mod; //(n-1)! % mod
|
||||
down = qmi(down, mod - 2); // 费马小定理求逆元
|
||||
|
||||
return up * down % mod; // 费马小定理
|
||||
}
|
||||
|
||||
signed main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 0; i < n; i++) cin >> A[i]; // 第i个盒子中有A[i]枝花,限制条件
|
||||
|
||||
// yxc这里写的代码太随意了,把我直接干蒙圈了!
|
||||
// 根据推导的式子,这里需要一个全部方案数=C(n + m - 1, n - 1)
|
||||
// 也就是说 res的初始值就是上面的全部方案数。
|
||||
// 可是,yxc大佬的大脑与正常人不一样,他居然没有给初始值,直接把初始值也写到下面的容斥原理代码中!!!
|
||||
// 也就是所有限制条件全部不采用,也就是全部不受限制!也就是全部方案数!!!
|
||||
int res = 0;
|
||||
for (int i = 0; i < 1 << n; i++) { // 容斥原理的项数,0000 代表所有限制条件都不遵守,0001代表第1个限制条件遵守,其它3个不遵守
|
||||
int sum = 0, cnt = 0; // 奇数个限制条件,需要减;偶数个限制条件,需要加。现在这种限制条件组合状态,是奇数个限制,还是偶数个限制?
|
||||
for (int j = 0; j < n; j++) // 枚举状态的每一位
|
||||
if (i >> j & 1) { // 如果此位是1
|
||||
sum += A[j] + 1; // 拼公式
|
||||
cnt++; // 限制条件个数,奇数个减,偶数个加
|
||||
}
|
||||
if (cnt & 1)
|
||||
res = (res - C(m + n - 1 - sum, n - 1) + mod) % mod;
|
||||
else
|
||||
res = (res + C(m + n - 1 - sum, n - 1)) % mod;
|
||||
}
|
||||
cout << (res + mod) % mod << endl;
|
||||
}
|
||||
```
|
||||
50
TangDou/AcWing_TiGao/T5/RongChi/214_1.cpp
Normal file
50
TangDou/AcWing_TiGao/T5/RongChi/214_1.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
const int N = 20, mod = 1e9 + 7;
|
||||
|
||||
int A[N];
|
||||
int n, m;
|
||||
|
||||
// 快速幂
|
||||
int qmi(int a, int k) {
|
||||
int res = 1;
|
||||
while (k) {
|
||||
if (k & 1) res = res * a % mod;
|
||||
a = a * a % mod;
|
||||
k >>= 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int C(int a, int b) {
|
||||
if (a < b) return 0;
|
||||
int up = 1, down = 1;
|
||||
for (int i = a; i > a - b; i--) up = i % mod * up % mod;
|
||||
for (int i = 1; i <= n - 1; i++) down = i * down % mod; //(n-1)! % mod
|
||||
down = qmi(down, mod - 2); // 费马小定理求逆元
|
||||
|
||||
return up * down % mod; // 费马小定理
|
||||
}
|
||||
|
||||
signed main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 0; i < n; i++) cin >> A[i];
|
||||
|
||||
int res = C(n + m - 1, n - 1);
|
||||
for (int i = 1; i < (1 << n); i++) {
|
||||
int sum = 0, cnt = 0;
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (i >> j & 1) {
|
||||
sum += A[j] + 1;
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt & 1)
|
||||
res = (res - C(m + n - 1 - sum, n - 1) + mod) % mod;
|
||||
else
|
||||
res = (res + C(m + n - 1 - sum, n - 1)) % mod;
|
||||
}
|
||||
cout << res << endl;
|
||||
}
|
||||
54
TangDou/AcWing_TiGao/T5/RongChi/214_2.cpp
Normal file
54
TangDou/AcWing_TiGao/T5/RongChi/214_2.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
const int N = 20, mod = 1e9 + 7;
|
||||
|
||||
int A[N];
|
||||
int n, m;
|
||||
|
||||
// 快速幂
|
||||
int qmi(int a, int k) {
|
||||
int res = 1;
|
||||
while (k) {
|
||||
if (k & 1) res = res * a % mod;
|
||||
a = a * a % mod;
|
||||
k >>= 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int C(int a, int b) {
|
||||
if (a < b) return 0;
|
||||
int up = 1, down = 1;
|
||||
for (int i = a; i > a - b; i--) up = i % mod * up % mod;
|
||||
for (int i = 1; i <= n - 1; i++) down = i * down % mod; //(n-1)! % mod
|
||||
down = qmi(down, mod - 2); // 费马小定理求逆元
|
||||
|
||||
return up * down % mod; // 费马小定理
|
||||
}
|
||||
|
||||
signed main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 0; i < n; i++) cin >> A[i]; // 第i个盒子中有A[i]枝花,限制条件
|
||||
|
||||
// yxc这里写的代码太随意了,把我直接干蒙圈了!
|
||||
// 根据推导的式子,这里需要一个全部方案数=C(n + m - 1, n - 1)
|
||||
// 也就是说 res的初始值就是上面的全部方案数。
|
||||
// 可是,yxc大佬的大脑与正常人不一样,他居然没有给初始值,直接把初始值也写到下面的容斥原理代码中!!!
|
||||
// 也就是所有限制条件全部不采用,也就是全部不受限制!也就是全部方案数!!!
|
||||
int res = 0;
|
||||
for (int i = 0; i < 1 << n; i++) { // 容斥原理的项数,0000 代表所有限制条件都不遵守,0001代表第1个限制条件遵守,其它3个不遵守
|
||||
int sum = 0, cnt = 0; // 奇数个限制条件,需要减;偶数个限制条件,需要加。现在这种限制条件组合状态,是奇数个限制,还是偶数个限制?
|
||||
for (int j = 0; j < n; j++) // 枚举状态的每一位
|
||||
if (i >> j & 1) { // 如果此位是1
|
||||
sum += A[j] + 1; // 拼公式
|
||||
cnt++; // 限制条件个数,奇数个减,偶数个加
|
||||
}
|
||||
if (cnt & 1)
|
||||
res = (res - C(m + n - 1 - sum, n - 1) + mod) % mod;
|
||||
else
|
||||
res = (res + C(m + n - 1 - sum, n - 1)) % mod;
|
||||
}
|
||||
cout << (res + mod) % mod << endl;
|
||||
}
|
||||
59
TangDou/AcWing_TiGao/T5/RongChi/215.cpp
Normal file
59
TangDou/AcWing_TiGao/T5/RongChi/215.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
const int N = 50010;
|
||||
|
||||
// 线性筛法求莫比乌斯函数(枚举约数)
|
||||
int mu[N], sum[N]; // 莫比乌斯函数的前缀和
|
||||
int primes[N], cnt;
|
||||
bool st[N];
|
||||
void get_mobius(int n) {
|
||||
mu[1] = 1;
|
||||
for (int i = 2; i <= n; i++) {
|
||||
if (!st[i]) {
|
||||
primes[cnt++] = i;
|
||||
mu[i] = -1;
|
||||
}
|
||||
for (int j = 0; primes[j] * i <= n; j++) {
|
||||
int t = primes[j] * i;
|
||||
st[t] = true;
|
||||
if (i % primes[j] == 0) {
|
||||
mu[t] = 0;
|
||||
break;
|
||||
}
|
||||
mu[t] = -mu[i];
|
||||
}
|
||||
}
|
||||
// 维护莫比乌斯函数前缀和
|
||||
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
|
||||
}
|
||||
|
||||
signed main() {
|
||||
// 筛法求莫比乌斯函数
|
||||
get_mobius(N - 1);
|
||||
|
||||
int T;
|
||||
cin >> T;
|
||||
|
||||
while (T--) {
|
||||
int a, b, d;
|
||||
cin >> a >> b >> d;
|
||||
// 套路啊,满满的套路,直接先用最大公约数a/gcd(a,b)=a',b/gcd(a,b)=b',映射到a',b'
|
||||
a /= d, b /= d;
|
||||
|
||||
// n为 min(a', b')
|
||||
int n = min(a, b);
|
||||
|
||||
int res = 0;
|
||||
|
||||
// l r, 是每一段的左右边界
|
||||
// 每次只能取较小的那个上界作为这一段的右端点r
|
||||
// 然后下次迭代时下一段的左端点就是r + 1
|
||||
for (int l = 1, r; l <= n; l = r + 1) { // 分块大法
|
||||
r = min(n, min(a / (a / l), b / (b / l)));
|
||||
res += (sum[r] - sum[l - 1]) * (a / l) * (b / l);
|
||||
}
|
||||
cout << res << endl;
|
||||
}
|
||||
}
|
||||
173
TangDou/AcWing_TiGao/T5/RongChi/215.md
Normal file
173
TangDou/AcWing_TiGao/T5/RongChi/215.md
Normal file
@@ -0,0 +1,173 @@
|
||||
##[$AcWing$ $215$. 破译密码](https://www.acwing.com/problem/content/description/217/)
|
||||
|
||||
|
||||
### 一、题目描述
|
||||
达达正在破解一段密码,他需要回答很多类似的问题:
|
||||
对于给定的整数 $a,b$ 和 $d$,有多少正整数对 $x,y$,满足 $x≤a,y≤b$,并且 $gcd(x,y)=d$。作为达达的同学,达达希望得到你的帮助。
|
||||
|
||||
**输入格式**
|
||||
第一行包含一个正整数 $n$,表示一共有 $n$ 组询问。
|
||||
|
||||
接下来 $n$ 行,每行表示一个询问,每行三个正整数,分别为 $a,b,d$。
|
||||
|
||||
**输出格式**
|
||||
对于每组询问,输出一个正整数,表示满足条件的整数对数。
|
||||
|
||||
**数据范围**
|
||||
$1≤n≤50000,1≤d≤a,b≤50000$
|
||||
|
||||
**输入样例:**
|
||||
```cpp {.line-numbers}
|
||||
2
|
||||
4 5 2
|
||||
6 4 3
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```cpp {.line-numbers}
|
||||
3
|
||||
2
|
||||
```
|
||||
|
||||
提示:$gcd(x,y)$ 返回 $x,y$ 的最大公约数。
|
||||
|
||||
### 二、题目分析
|
||||
|
||||
#### 前导知识
|
||||
**[$1$、容斥原理 $AcWing$ $890$. 能被整除的数 ](https://www.cnblogs.com/littlehb/p/15389237.html)**
|
||||
> **小结**:
|
||||
> ① 二进制对质数序列的组合式枚举
|
||||
> ② 奇数加,偶数减
|
||||
|
||||
**[$2$、数论分块 $AcWing$ $199$. 余数之和](https://www.cnblogs.com/littlehb/p/16389420.html)**
|
||||
> **小结**:
|
||||
> 给出$n,k$,求 $\displaystyle \sum_{i=1}^n k \ \% \ i$
|
||||
|
||||
**核心代码**
|
||||
```cpp {.line-numbers}
|
||||
ans = n * k; // 看题解的推导公式
|
||||
for (l = 1; l <= n; l = r + 1) { // 枚举左端点,每次跳着走,下次的位置就是本次r的位置+1
|
||||
if (k / l == 0) break; // 1、当k/l=0的时候,显然这段以及后面(有单调性)已经没有贡献了,可以 break。
|
||||
r = min(k / (k / l), n); // 2、注意右端点和n取个min,因为>n没有贡献了。
|
||||
ans -= (k / l) * (l + r) * (r - l + 1) / 2; // 等差数列求和:左到右边界内,是公差为1的等差数列,首项+末项 乘以 项数 除以2
|
||||
}
|
||||
```
|
||||
|
||||
**[$3$、莫比乌斯函数](https://www.cnblogs.com/littlehb/p/16392135.html)**
|
||||
|
||||
#### 解题思路
|
||||
我们分析一下,题目很明了,要求的就是$gcd(x,y)=d$的个数,转化一下:
|
||||
$$\large x'=x/d,y'=y/d$$
|
||||
|
||||
$$\large |gcd(x,y)=d| \Leftrightarrow |gcd(x',y')=1|$$
|
||||
|
||||
><font color='red'><b>注</b>:上面的"| |",表示的是符合这样条件的$(x,y)$的数对个数,这两个式子的数对$(x,y)$和$(x',y')$ 是一一对应的。</font>
|
||||
|
||||
> **证明**:
|
||||
首先,$x , y$ 的最大公约数是$d$,那么 **除去最大公约数**,得到的两个数就 **互质**。对于每个$gcd(x,y)=d$都如此操作,那么可以证明,这两个的 **数量** 是一一对应的。
|
||||
|
||||
问题转化为:$x≤a/d$ , $y≤b/d$ ,$gcd(x,y)=1$的个数,我们考虑:
|
||||
|
||||
<h4>补集思想:互质数 = 总数 - 不互质数</h4>
|
||||
|
||||
总数很容易,$a'=a/d,b'=b/d$ ,两个数的所有组合形式数量就是$a'∗b'$,下面分析下我们要减去不互质的数:
|
||||
|
||||
$$\large a'b'-⌊\frac{a'}{2}⌋⌊\frac{b'}{2}⌋-⌊\frac{a'}{3}⌋⌊\frac{b'}{3}⌋-...\\
|
||||
\ \ \ \ \ \ \ +⌊\frac{a'}{6}⌋⌊\frac{b'}{2}⌋+.... \\
|
||||
\ \ \ \ \ \ \ -.....
|
||||
$$
|
||||
|
||||
这样看来,本题是一个简单的容斥原理!
|
||||
|
||||
|
||||
但是由于容斥原理是$O(N)$的,询问的次数$50000$次,会超时,才会想到需要优化。
|
||||
|
||||
**优化办法**
|
||||
|
||||
① 莫比乌斯函数
|
||||
$$\displaystyle \large a' \cdot b'+\sum_{i=1}^{min(a,b)}⌊\frac{a'}{i}⌋⌊\frac{b'}{i}⌋ \cdot {mobius[i]}$$
|
||||
|
||||
② 数据分块
|
||||
参考余数之和。
|
||||
|
||||
|
||||
$$\large \sum_{i=2}^{min(a',b')}⌊\frac{a'}{i}⌋*⌊\frac{b'}{i}⌋*mobius[i]$$
|
||||
|
||||
> **证明**:
|
||||
> 我们要减去的是什么呢?是不互质的数,不互质数是什么呢?是最大公因数不为$1$的数,不为$1$,那可以为几呢?**不是$1$就行**(~~废话~~),**枚举最大公因数不为$1$的个数即可**,枚举每一个可能的最大公因数,并且计算个数,其中 $2 <= i <= min(a',b')$ 。
|
||||
>
|
||||
当最大公因数是只有一个质因子的时候,我们可以列出:
|
||||
$$\large ⌊\frac{a'}{2}⌋* ⌊\frac{b'}{2}⌋+⌊\frac{a'}{3}⌋*⌊\frac{b'}{3}⌋+.....$$
|
||||
最大公因数是$2$的两对数,实际上就等于$2$的倍数个数相乘,这两个个数是等同的,同理$3$也是,但是我们要考虑重复的情况,如$6$,我们$2,3$,都枚举了一次,那么会重复,根据容斥原理,我们要减去,最后我们发现,其实符号就是$Mobius$函数(这个证明是显然的)。以此类推,枚举到$a′$和$b′$中较小的就可以了,因为大于较小的,则式子为$0$,我们就能推出上面的式子了。
|
||||
|
||||
答案呼之欲出:
|
||||
$$\large a'*b'+\sum_{i=2}^{min(a',b')}⌊\frac{a'}{i}⌋*⌊\frac{b'}{i}⌋*mobius[i]$$
|
||||
|
||||
我们把这玩意合体一下
|
||||
$$\large \sum_{i=1}^{min(a',b')}⌊\frac{a'}{i}⌋*⌊\frac{b'}{i}⌋*mobius[i]$$
|
||||
|
||||
- $\large ⌊\frac{a}{i}⌋×⌊\frac{b}{i}⌋$ 对应的序列有很多值都是相同的,计算一次就行(**[数论分块](https://www.cnblogs.com/littlehb/p/16389420.html)** 优化一下),假设值记为$t$,对应的区间假设为$[l, r]$,然后让$t$乘以$μ(l)+μ(l+1)+…+μ(r)$ 即可求出该段中的和,$μ(l)+μ(l+1)+…+μ(r)$,可以使用 **莫比乌斯前缀和** 求解。
|
||||
|
||||
|
||||
### $Code$
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
const int N = 50010;
|
||||
|
||||
// 线性筛法求莫比乌斯函数(枚举约数)
|
||||
int mu[N], sum[N]; // 莫比乌斯函数的前缀和
|
||||
int primes[N], cnt;
|
||||
bool st[N];
|
||||
void get_mobius(int n) {
|
||||
mu[1] = 1;
|
||||
for (int i = 2; i <= n; i++) {
|
||||
if (!st[i]) {
|
||||
primes[cnt++] = i;
|
||||
mu[i] = -1;
|
||||
}
|
||||
for (int j = 0; primes[j] * i <= n; j++) {
|
||||
int t = primes[j] * i;
|
||||
st[t] = true;
|
||||
if (i % primes[j] == 0) {
|
||||
mu[t] = 0;
|
||||
break;
|
||||
}
|
||||
mu[t] = -mu[i];
|
||||
}
|
||||
}
|
||||
// 维护莫比乌斯函数前缀和
|
||||
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
|
||||
}
|
||||
|
||||
signed main() {
|
||||
// 筛法求莫比乌斯函数
|
||||
get_mobius(N - 1);
|
||||
|
||||
int T;
|
||||
cin >> T;
|
||||
|
||||
while (T--) {
|
||||
int a, b, d;
|
||||
cin >> a >> b >> d;
|
||||
// 套路啊,满满的套路,直接先用最大公约数a/gcd(a,b)=a',b/gcd(a,b)=b',映射到a',b'
|
||||
a /= d, b /= d;
|
||||
|
||||
// n为 min(a', b')
|
||||
int n = min(a, b);
|
||||
|
||||
int res = 0;
|
||||
|
||||
// l r, 是每一段的左右边界
|
||||
// 每次只能取较小的那个上界作为这一段的右端点r
|
||||
// 然后下次迭代时下一段的左端点就是r + 1
|
||||
for (int l = 1, r; l <= n; l = r + 1) { // 分块大法
|
||||
r = min(n, min(a / (a / l), b / (b / l)));
|
||||
res += (sum[r] - sum[l - 1]) * (a / l) * (b / l);
|
||||
}
|
||||
cout << res << endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
55
TangDou/AcWing_TiGao/T5/RongChi/215_0.cpp
Normal file
55
TangDou/AcWing_TiGao/T5/RongChi/215_0.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
typedef long long LL;
|
||||
const int N = 1e4 + 5;
|
||||
|
||||
// 例题:HDU4135 HDU2841,HDU1695,HDU3501
|
||||
|
||||
LL n, m;
|
||||
// m 比较大的话可以先打出来素数表再求
|
||||
|
||||
// 分解质因数
|
||||
int p[N], cnt;
|
||||
void divide(int x) {
|
||||
cnt = 0;
|
||||
for (int i = 2; i <= x / i; i++) {
|
||||
if (x % i == 0) {
|
||||
p[cnt++] = i;
|
||||
while (x % i == 0) x /= i;
|
||||
}
|
||||
}
|
||||
if (x > 1) p[cnt++] = x;
|
||||
}
|
||||
|
||||
LL solve() {
|
||||
LL ans = 0;
|
||||
for (int i = 1; i < (1 << cnt); i++) { // 二进制枚举质因子所有组合
|
||||
LL t = 1, cnt = 0;
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
if (i >> j & 1) {
|
||||
t *= p[j];
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
ans += (cnt & 1 ? n / t : -n / t); // 奇加偶减
|
||||
}
|
||||
return n - ans;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// 求1~10中与3互质的数的个数
|
||||
/*
|
||||
测试用例:
|
||||
10 3
|
||||
|
||||
人脑模拟:1,2,4,5,7,8,10 ,共7个
|
||||
程序输出结果:7
|
||||
结论:一致,代码应该是正确的
|
||||
*/
|
||||
cin >> n >> m;
|
||||
// 对m分解质因数
|
||||
divide(m);
|
||||
|
||||
cout << solve() << endl;
|
||||
return 0;
|
||||
}
|
||||
71
TangDou/AcWing_TiGao/T5/RongChi/HDU1695.cpp
Normal file
71
TangDou/AcWing_TiGao/T5/RongChi/HDU1695.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
|
||||
// 返回1-m中与n互素的数的个数
|
||||
vector<int> p;
|
||||
int cal(int n, int m) {
|
||||
p.clear();
|
||||
for (int i = 2; i * i <= n; i++) {
|
||||
if (n % i == 0) {
|
||||
p.push_back(i);
|
||||
while (n % i == 0) n /= i;
|
||||
}
|
||||
}
|
||||
if (n > 1) p.push_back(n); // 求n的素因子
|
||||
|
||||
int s = 0; // 1到m中与n不互素的数的个数
|
||||
|
||||
// 枚举子集,不能有空集,所以从1开始
|
||||
for (int i = 1; i < 1 << p.size(); i++) { // 从1枚举到(2^素因子个数)
|
||||
int cnt = 0;
|
||||
int t = 1;
|
||||
for (int j = 0; j < p.size(); j++) { // 枚举每个素因子
|
||||
if (i & (1 << j)) { // 有第i个因子
|
||||
cnt++; // 计数
|
||||
t *= p[j]; // 乘上这个质因子
|
||||
}
|
||||
}
|
||||
// 容斥原理
|
||||
if (cnt & 1) // 选取个数为奇数,加
|
||||
s += m / t;
|
||||
else // 选取个数为偶数,减
|
||||
s -= m / t;
|
||||
}
|
||||
return m - s; // 返回1-m中与n互素的数的个数
|
||||
}
|
||||
|
||||
int T, ca;
|
||||
signed main() {
|
||||
cin >> T;
|
||||
while (T--) {
|
||||
int a, b, c, d, k;
|
||||
cin >> a >> b >> c >> d >> k;
|
||||
// k=0时需要特判,因为我们想要 x'=x/k ,y'=y/k,不能随意除,需要判断
|
||||
if (k == 0) { // k=0时,表示gcd(x,y)=0
|
||||
/*
|
||||
如果两个数的最大公约数是0,这意味着这两个数中至少有一个数是0。
|
||||
因为最大公约数是两个数共有的最大因子,而0没有最大因子,所以0
|
||||
与任何数的最大公约数都是0。
|
||||
而a<=x<=b,c<=y<=d,a=c=1,所以,k=0是不可能存在gcd(x,y)=0的,应该返回0对
|
||||
*/
|
||||
printf("Case %lld: 0\n", ++ca);
|
||||
continue;
|
||||
}
|
||||
b /= k, d /= k;
|
||||
// 因为 (1,3)与 (3,1)算1个,所以要限制x<y
|
||||
// a=c=1
|
||||
if (b > d) swap(d, b);
|
||||
int ans = 0;
|
||||
// d>b
|
||||
for (int i = 1; i <= d; i++) // 枚举大区间
|
||||
// c(n,m): 返回1-m中与n互素的数的个数
|
||||
// 拿大区间[1~d]中的每个数字i,去 [1~b]中找与其互质的数
|
||||
// 但是,这样做的话,会出现 [1,3],[3,1]这样的情况,为了防止这样的事情发生
|
||||
// 我们需要控制区间的范围,也就是小于等于i,同时,也要考虑i与b的大小关系,保证i<=b
|
||||
// 也就是 min(i,b)
|
||||
ans += cal(i, min(i, b));
|
||||
printf("Case %lld: %lld\n", ++ca, ans);
|
||||
}
|
||||
}
|
||||
51
TangDou/AcWing_TiGao/T5/RongChi/HDU2841_1.cpp
Normal file
51
TangDou/AcWing_TiGao/T5/RongChi/HDU2841_1.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
const int N = 1e6 + 10;
|
||||
|
||||
// 返回1-m中与n互素的数的个数
|
||||
vector<int> p;
|
||||
int cal(int n, int m) {
|
||||
p.clear();
|
||||
for (int i = 2; i * i <= n; i++) {
|
||||
if (n % i == 0) {
|
||||
p.push_back(i);
|
||||
while (n % i == 0) n /= i;
|
||||
}
|
||||
}
|
||||
if (n > 1) p.push_back(n); // 求n的素因子
|
||||
|
||||
int s = 0; // 1到m中与n不互素的数的个数
|
||||
|
||||
// 枚举子集,不能有空集,所以从1开始
|
||||
for (int i = 1; i < 1 << p.size(); i++) { // 从1枚举到(2^素因子个数)
|
||||
int cnt = 0;
|
||||
int t = 1;
|
||||
for (int j = 0; j < p.size(); j++) { // 枚举每个素因子
|
||||
if (i & (1 << j)) { // 有第i个因子
|
||||
cnt++; // 计数
|
||||
t *= p[j]; // 乘上这个质因子
|
||||
}
|
||||
}
|
||||
// 容斥原理
|
||||
if (cnt & 1) // 选取个数为奇数,加
|
||||
s += m / t;
|
||||
else // 选取个数为偶数,减
|
||||
s -= m / t;
|
||||
}
|
||||
return m - s; // 返回1-m中与n互素的数的个数
|
||||
}
|
||||
|
||||
signed main() {
|
||||
int T;
|
||||
cin >> T;
|
||||
while (T--) {
|
||||
int a, b;
|
||||
cin >> a >> b;
|
||||
int res = 0;
|
||||
// 从1~n(b现在就是n)之间,找到所有与m(m现在就是i)互质的数字个数
|
||||
for (int i = 1; i <= a; i++) res += cal(i, b);
|
||||
printf("%lld\n", res);
|
||||
}
|
||||
}
|
||||
90
TangDou/AcWing_TiGao/T5/RongChi/HDU2841_2.cpp
Normal file
90
TangDou/AcWing_TiGao/T5/RongChi/HDU2841_2.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
typedef long long LL;
|
||||
const int N = 1e5 + 10;
|
||||
|
||||
// 性能非常棒,但是没看懂,等功能加深后再回来看看。
|
||||
// 据说这道题,还可以用莫比乌斯反演来做,以后再
|
||||
// 筛法求欧拉函数
|
||||
int primes[N]; //保存的是每一个质数
|
||||
int cnt; // cnt代表质数下标,就是到第几个了
|
||||
int phi[N]; //欧拉函数值,一般叫Φ,函数名不能是希腊字母,所以转为phi
|
||||
bool st[N]; //代表是不是已经被筛掉了
|
||||
LL res; //结果
|
||||
void get_eulers(int n) {
|
||||
// 1的欧拉函数值
|
||||
phi[1] = 1;
|
||||
//如果当前i没有被筛过,说明当前i是一个质数
|
||||
for (int i = 2; i <= n; i++) {
|
||||
if (!st[i]) {
|
||||
//增加一个质数
|
||||
primes[cnt++] = i;
|
||||
phi[i] = i - 1;
|
||||
}
|
||||
for (int j = 0; primes[j] <= n / i; j++) {
|
||||
int t = primes[j] * i;
|
||||
st[t] = true;
|
||||
if (i % primes[j] == 0) {
|
||||
phi[t] = phi[i] * primes[j];
|
||||
break;
|
||||
} else
|
||||
phi[t] = phi[i] * (primes[j] - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int n, m;
|
||||
int num, p[10], sum, i;
|
||||
|
||||
void dfs(int pos, int lcm, int id) {
|
||||
if (id == 0) {
|
||||
if (i & 1)
|
||||
num += (m / lcm);
|
||||
else
|
||||
num -= (m / lcm);
|
||||
return;
|
||||
}
|
||||
if (pos > sum) return;
|
||||
if (lcm * p[pos] <= m)
|
||||
dfs(pos + 1, lcm * p[pos], id - 1);
|
||||
dfs(pos + 1, lcm, id);
|
||||
return;
|
||||
}
|
||||
|
||||
int solve(int k) {
|
||||
sum = 0;
|
||||
//下面是分解质数
|
||||
for (int i = 2; i <= k / i; i++) {
|
||||
if (k % i == 0) {
|
||||
p[++sum] = i;
|
||||
while (k % i == 0) k /= i;
|
||||
}
|
||||
}
|
||||
if (k > 1) p[++sum] = k;
|
||||
|
||||
for (i = 1; i <= sum; i++) dfs(1, 1, i);
|
||||
return m - num;
|
||||
}
|
||||
|
||||
int main() {
|
||||
//筛法求欧拉函数
|
||||
get_eulers(N - 10);
|
||||
|
||||
int T;
|
||||
scanf("%d", &T);
|
||||
while (T--) {
|
||||
scanf("%d%d", &n, &m); // n行m列
|
||||
if (m > n) swap(n, m);
|
||||
|
||||
LL res = 1;
|
||||
for (int i = 2; i <= m; i++) res += 2 * phi[i]; //这是啥意思呢?
|
||||
|
||||
for (int i = m + 1; i <= n; i++) {
|
||||
num = 0;
|
||||
res += solve(i);
|
||||
}
|
||||
|
||||
printf("%lld\n", res);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
64
TangDou/AcWing_TiGao/T5/RongChi/HDU3501_1.cpp
Normal file
64
TangDou/AcWing_TiGao/T5/RongChi/HDU3501_1.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
const int mod = 1000000007;
|
||||
|
||||
signed main() {
|
||||
int m;
|
||||
while (cin >> m && m) {
|
||||
if (m == 1) {
|
||||
cout << "0" << endl;
|
||||
// 边界需要特别注意!本题的左边界是1
|
||||
// 表示的含义是小于1,并且与1不互质的数的加法和,当然是0。
|
||||
// 在做题时,先想正常思路,然后再思考一下边界是不是用特判。
|
||||
continue;
|
||||
}
|
||||
|
||||
int n = m; // 复制出来进行质因数分解
|
||||
|
||||
// 分解质因数
|
||||
vector<int> p;
|
||||
for (int i = 2; i * i <= n; i++) {
|
||||
if (n % i == 0) {
|
||||
p.push_back(i);
|
||||
while (n % i == 0) n = n / i;
|
||||
}
|
||||
}
|
||||
if (n > 1) p.push_back(n);
|
||||
|
||||
// 容斥原理
|
||||
int s = 0;
|
||||
for (int i = 1; i < (1 << p.size()); i++) {
|
||||
int cnt = 0;
|
||||
int t = 1;
|
||||
for (int j = 0; j < p.size(); j++) {
|
||||
if (i >> j & 1) {
|
||||
cnt++;
|
||||
t *= p[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* t:质因子的累乘积
|
||||
cnt:质因子的个数
|
||||
举栗子:
|
||||
枚举到的组合中只有数字2,那么2,4,6,8,10,....需要加上
|
||||
枚举到的组合中只有数字3,那么3,6,9,12,15,....需要加上
|
||||
枚举到的组合中中6.有数字2,3,那么6,12,18,....需要减去
|
||||
*/
|
||||
int num = (m - 1) / t; // 题目要求:比m小的数字中,也就是[1~m-1]这些数字中有多少个数是t的倍数呢?是(m-1)/t个。
|
||||
/* 这些数字,首个是t,第二个是t*2,最后一个是t*num
|
||||
等差数列求和公式:(首项+末项)*项数/2
|
||||
模板题是计数,这里不是计数,而是计算这些数加在一起是多少,不能傻傻的一个个累加,而是采用了数学中的等差数列求和公式,
|
||||
否则聪明的高斯该生气了~
|
||||
*/
|
||||
int tmp = (t + t * num) * num / 2;
|
||||
|
||||
if (cnt & 1) // 奇数的加
|
||||
s += tmp;
|
||||
else // 偶数的减
|
||||
s -= tmp;
|
||||
}
|
||||
cout << s % mod << endl; // 取模输出
|
||||
}
|
||||
}
|
||||
46
TangDou/AcWing_TiGao/T5/RongChi/HDU3501_2.cpp
Normal file
46
TangDou/AcWing_TiGao/T5/RongChi/HDU3501_2.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
long long fac[500];
|
||||
int cnt;
|
||||
void fen(int n) {
|
||||
for (int i = 2; i * i <= n; i++) {
|
||||
if (n % i == 0) {
|
||||
fac[cnt++] = i;
|
||||
while (n % i == 0) n /= i;
|
||||
}
|
||||
}
|
||||
if (n > 1) fac[cnt++] = n;
|
||||
} //素因子分解
|
||||
long long ans;
|
||||
int n;
|
||||
const int mod = 1000000007;
|
||||
void dfs(int w, int id, long long sum) {
|
||||
for (int i = w; i < cnt; i++) {
|
||||
long long ss = sum * fac[i];
|
||||
if (id & 1) {
|
||||
int tmp = n / ss;
|
||||
ans += ss * (1 + tmp) * tmp / 2;
|
||||
ans %= mod;
|
||||
} else {
|
||||
int tmp = n / ss;
|
||||
ans -= ss * (1 + tmp) * tmp / 2;
|
||||
ans = (ans + mod) % mod;
|
||||
}
|
||||
dfs(i + 1, id + 1, ss);
|
||||
}
|
||||
} //递归实现容斥原理
|
||||
int main() {
|
||||
while (scanf("%d", &n) != EOF) {
|
||||
if (n == 0) break;
|
||||
if (n == 1) {
|
||||
cout << 0 << endl;
|
||||
continue;
|
||||
}
|
||||
cnt = 0;
|
||||
fen(n);
|
||||
ans = 0;
|
||||
dfs(0, 1, 1);
|
||||
cout << (ans % mod - n + mod) % mod << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
26
TangDou/AcWing_TiGao/T5/RongChi/HDU3501_Euler.cpp
Normal file
26
TangDou/AcWing_TiGao/T5/RongChi/HDU3501_Euler.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
typedef unsigned long long ULL;
|
||||
const ULL mod = 1000000007;
|
||||
|
||||
ULL euler(ULL n) {
|
||||
ULL res = n, a = n;
|
||||
for (ULL i = 2; i * i <= a; i++) {
|
||||
if (a % i == 0) {
|
||||
res = res / i * (i - 1);
|
||||
while (a % i == 0) a /= i;
|
||||
}
|
||||
}
|
||||
if (a > 1) res = res / a * (a - 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
int main() {
|
||||
ULL n;
|
||||
while (~scanf("%llu", &n), n) {
|
||||
ULL sum = n * (1 + n) / 2 - n, ans;
|
||||
ans = sum - euler(n) * n / 2;
|
||||
printf("%llu\n", ans % mod);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
51
TangDou/AcWing_TiGao/T5/RongChi/HDU4135.cpp
Normal file
51
TangDou/AcWing_TiGao/T5/RongChi/HDU4135.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
#define int long long
|
||||
#define endl "\n"
|
||||
|
||||
vector<int> p; // 质数数组
|
||||
int cal(int n, int m) { // 返回1-m中与n互素的数的个数
|
||||
p.clear(); // 多组测试数据,清空
|
||||
// 分解质因数
|
||||
for (int i = 2; i * i <= n; i++) { // 对n分解质因数
|
||||
if (n % i == 0) {
|
||||
p.push_back(i);
|
||||
while (n % i == 0) n /= i;
|
||||
}
|
||||
}
|
||||
if (n > 1) p.push_back(n); // 最后一个大因子,也加入
|
||||
|
||||
int s = 0; // 1到m中与n不互素的数的个数
|
||||
|
||||
// 枚举子集,不能有空集,所以从1开始
|
||||
for (int i = 1; i < 1 << p.size(); i++) { // 从1枚举到(2^素因子个数)
|
||||
int cnt = 0;
|
||||
int t = 1;
|
||||
for (int j = 0; j < p.size(); j++) { // 枚举每个素因子
|
||||
if (i & (1 << j)) { // 有第i个因子
|
||||
cnt++; // 计数
|
||||
t *= p[j]; // 乘上这个质因子
|
||||
}
|
||||
}
|
||||
// 容斥原理
|
||||
if (cnt & 1) // 选取个数为奇数,加
|
||||
s += m / t;
|
||||
else // 选取个数为偶数,减
|
||||
s -= m / t;
|
||||
}
|
||||
return m - s; // 返回1-m中与n互素的数的个数
|
||||
}
|
||||
|
||||
signed main() {
|
||||
int T, ca = 0;
|
||||
cin >> T;
|
||||
|
||||
while (T--) {
|
||||
int m, a, b;
|
||||
cin >> a >> b >> m; // 求区间[a,b]中与m互素的数字个数
|
||||
// 计算[1,a-1]之间与m互素的个数
|
||||
// 计算[1, b]之间与m互素的个数
|
||||
int ans = cal(m, b) - cal(m, a - 1);
|
||||
printf("Case #%d: %lld\n", ++ca, ans);
|
||||
}
|
||||
}
|
||||
85
TangDou/AcWing_TiGao/T5/RongChi/UVA11806.cpp
Normal file
85
TangDou/AcWing_TiGao/T5/RongChi/UVA11806.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
const int mod = 1000007;
|
||||
const int N = 410;
|
||||
int C[N][N];
|
||||
int n, m, k, ans;
|
||||
/*
|
||||
Sample Input
|
||||
2
|
||||
2 2 1
|
||||
2 3 2
|
||||
Sample Output
|
||||
Case 1: 0
|
||||
Case 2: 2
|
||||
*/
|
||||
int main() {
|
||||
#ifndef ONLINE_JUDGE
|
||||
freopen("UVA11806.in", "r", stdin);
|
||||
#endif
|
||||
// 预处理出组合数结果数组
|
||||
for (int i = 1; i < N; i++) {
|
||||
C[i][0] = C[i][i] = 1;
|
||||
for (int j = 1; j < i; j++)
|
||||
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
|
||||
}
|
||||
|
||||
int T, cas = 1;
|
||||
int S, s1, s2, s3, s4;
|
||||
cin >> T;
|
||||
while (T--) {
|
||||
ans = 0; // 多组测试数据,每次注意清零
|
||||
cin >> n >> m >> k; // n行,m列,k个人
|
||||
if (k == 0) { // 一定要注意边界情况,比如0个人
|
||||
printf("Case %d: 0\n", cas++);
|
||||
continue;
|
||||
}
|
||||
|
||||
S = C[n * m][k]; // n*m个格子中找出k个格子站人,就是所有方案数
|
||||
/*
|
||||
S为总数
|
||||
A为第一行没有站人
|
||||
B为最后一行没有站人
|
||||
C为第一列没有站人
|
||||
D为最后一列没有站人
|
||||
|
||||
令:
|
||||
s1 =(A+B+C+D)
|
||||
s2=(AB+AC+AD+BC+BD+CD)
|
||||
s3=(ABC+ABD+ACD+BCD)
|
||||
s4=(ABCD)
|
||||
*/
|
||||
|
||||
// A:第一行没人,即C[(n-1)*m,k]
|
||||
// B:最后一行没人,即C[(n-1)*m,k]
|
||||
// C:第一列没人,即:C[n*(m-1),k]
|
||||
// D:最后一列没人,即:C[n*(m-1),k]
|
||||
s1 = 2 * (C[n * (m - 1)][k] + C[(n - 1) * m][k]) % mod;
|
||||
|
||||
// AB:第一行,最后一行没有人,行少了2行,列不变,即C[(n-2)*m,k]
|
||||
// AC:第一行,第一列没有人,行少了1行,列少了一列,即C[(n-1)*(m-1),k]
|
||||
// AD:第一行,最后一列没有人,即C[(n-1)*(m-1),k]
|
||||
// BC:最后一行,第一列没有人,即C[(n-1)*(m-1),k]
|
||||
// BD:最后一行,最后一列没有人,即C[(n-1)*(m-1),k]
|
||||
// CD:第一列,最后一列没有人,即C[n*(m-2),k]
|
||||
// 中间4个是一样的:4*C[(n-1)*(m-1),k]
|
||||
s2 = (C[(n - 2) * m][k] + 4 * C[(n - 1) * (m - 1)][k] + C[n * (m - 2)][k]) % mod;
|
||||
|
||||
// ABC:第一行、最后一行、第一列没有人,行少了2行,列少了1列,即C[(n-2)*(m-1),k]
|
||||
// ABD:第一行、最后一行、最后一列没有人,行少了2行,列少了1列,即C[(n-2)*(m-1),k]
|
||||
// ABC+ABD=2*C[(n-2)*(m-1),k]
|
||||
// ACD:第一行、第一列、最后一列没有人,行少了1行,列少了2列,即 C[(n-1)*(m-2),k]
|
||||
// BCD:最后一行、第一列、最后一列没有人,行少了1行,列少了2列,即 C[(n-1)*(m-2),k]
|
||||
// ACD+BCD=2*C[(n-1)*(m-2),k]
|
||||
s3 = 2 * (C[(n - 2) * (m - 1)][k] + C[(n - 1) * (m - 2)][k]) % mod;
|
||||
|
||||
// 第一行,第一列,最后一行,最后一列都不能站,那么,剩下n-2行,m-2列,需要在(n-2)*(m-2)这么多的格子里找出k个格子
|
||||
s4 = C[(n - 2) * (m - 2)][k] % mod;
|
||||
|
||||
ans = (S - s1 + s2 - s3 + s4) % mod; // 容斥原理
|
||||
ans = (ans + mod) % mod; // 防止取余后出现负数
|
||||
printf("Case %d: %d\n", cas++, ans); // 输出答案
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
3
TangDou/AcWing_TiGao/T5/RongChi/UVA11806.in
Normal file
3
TangDou/AcWing_TiGao/T5/RongChi/UVA11806.in
Normal file
@@ -0,0 +1,3 @@
|
||||
2
|
||||
2 2 1
|
||||
2 3 2
|
||||
Reference in New Issue
Block a user