#include
using namespace std;
简单数论
整除 - 分解质因数
质数
积性函数
模算术
例题
公约数-公倍数-唯一分解定理
最大公约数:
显然 gcd(a,b) == gcd(b,a) 性质: gcd(a,b) = gcd(b,a-b) = gcd(b,a mod b);
gcd(a,b) = 1时 a,b互质
最小公倍数:
显然lcm(a,b) = lcm(b,a)
lcm(a,b) = a * b / gcd(a,b) = a / gcd(a,b) * b 先除再乘防止数值爆炸!!!
质数:设 n >= 2 ,所有满足 [1,n]的数不是n的约数,则称n为质数 ,否则称n为合数
唯一分解定理(分解质因数):设 n >= 2 ,n = ∏(i = 1 --> m) p[i]k[i] (p[1 --> m]为质数)
质因数分解
#include<vector>
//质因数分解
vector<int> factor(int n) {
vector<int> f;
for(int i = 2; i * i <= n; i++) {
while(n % i == 0) { //是质数且能整除n ,每个因数还可以多除几次直到不能整除
f.push_back(i);
n /= i; //注意到n会变小,如果n小于i*i,n剩下的最后肯定是一个质数,也要存入
}
}
if(n > 1) { //存最后的数 , f中 所有数相乘 = = n
f.push_back(n);
}
return f;
}
int test_01() {
int n;
cin >> n;
for(int i = 0; i < factor(n).size(); i++) {
cout << factor(n)[i] << endl;
}
return 0;
}
解法三 : p质数存表,枚举质数n%p[i]
vector p;
vector factor2(int n) {
bool flag = true;
for(int i = 2; i<= 100; i++) {
for(int j = 2; j * j <= i ;j++) {
if(i % j == 0) {
flag = false;
break;
}
}
if(flag) {
p.push_back(i);
}
flag = true;//恢复
}
vector f;
for(int i = 0; i < p.size(); i++) {
if(p[i] * p[i] > n) {
break;
}
while(n % p[i] == 0) {
f.push_back(p[i]);
n /= p[i];
}
}
if(n > 1) {
f.push_back(n);
}
return f;
}
/*欧拉质数筛法 --i * p[j]的质因子一定小于p[j],如果枚举到i,若i没有被标记,则i为质数
const int N = 100000;
bool vis[N + 1];
vector p;
void sieve() {
for(int i = 2; i <= N; i++) {
if(!vis[i]) {
p.push_back(i);
}
for(int j = 0; i * p[j] <= N + 1; j++) {
vis[i * p[j]] = 1;
if(i % p[j] == 0) {
break;
}
}
}
break;
}
*/
/积性函数
如果f(1) = 1 ,且对于a,b互质 , f(ab) = f(a) * f(b)
欧拉 φ函数 ,约数函数
模乘法的逆元 (密码学嘿嘿)
a * b mod n = 1 ,则称b为a模n的逆元 记为a-1
当且仅当gcd(a,n) = 1 时 (a,n互质时) ,a模n的逆元才存在!!!
b1,b2为a的逆元,则必有b1 ,与b2同模于n
费马小定理 设p为质数,a为与p互质的整数,则有 a^p - 1^ 模p 余1
欧拉定理
…
例题
…
图论
P3387 缩点 + dp
…
第十一届
简单序列题
1.有一个序列,序列的第一个数是n,后面的每个数是前一数整除2,请输出这个序列中值为正数的项
AcWing
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
int test_02(){
ll n;
cin >> n;
while(n){
cout << n << " ";
n /= 2;
}
return 0;
}
2066解码
H3el5o2 还原为 HHHellllloo
#include<iostream>
#include<cstring>
using namespace std;
int test_03(){
string s,res; //res存答案
cin >> s;
for(int i = 0;i < s.size();i++){ // 读入s
if(i + 1 < s.size() && s[i + 1] <= '9'){ //在范围内 ,且读到数字时
int k = s[i + 1] - '0';
while(k--){
res += s[i]; //字符串拼接
}
i++;//因为此次操作读取2位,包括了字母 + 数字位
}else{
res += s[i]; //下一位不是数字,就只加一次
}
}
cout << res << endl;
return 0;
}
2067走方格 (类似数字三角形) 闫式dp分析法
左上角走到右下角 ,每次选下或右走
行号和列号都为偶数(不能走),问有多少种走法
#include<iostream>
#include<cstring>
using namespace std;
const int N_04 = 40;
int n_04,m_04;
int f[N_04][N_04];
int test_04(){
cin >> n_04 >> m_04;
f[1][1] = 1;
for(int i = 1;i <= n_04;i++){
for(int j = 1;j <= m_04;j++){
if(i == 1 && j == 1)continue; //为起点
if(i % 2 || j % 2){ //i%2 或 j%2 余 1时执行 即为奇数时执行 (等效 i,j不能同时为偶数 ) ,i,j不能都为偶数正向思维: !(i % 2 = = 0 && j % 2 == 0)
f[i][j] = f[i - 1][j] + f[i][j - 1]; //从哪里来 ,即上一步哪些位置能走到当前位置
}
}
}
cout << f[n_04][m_04] << endl;
return 0;
}
整数拼接 (难 * – 先跳过)
给定长度为n的数组,从中选出两个数,拼成一个新的整数
例如 12 或 345 可以拼成 12345 或 34512
注意交换A[i]与A[j]属于两种拼法,即使两者相同
计算有多少种拼法是整数k的倍数
输入:
n k [1,1e5]
A[i]…
输出:
ans
相当于 k | A[j] * 10k[i] + A[j] 等效乘10 ,再加
11个hash表A[j] * 100 … A[j] 1010
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int n,m;
int a[N] , s[11][N];
int test_05(){
scanf("%d%d",&n,&m);
for(int i = 0;i < n;i++)scanf("%d",&a[i]);//读入数据
for(int i = 0;i < n;i++){//预处理hash表
LL t = a[i] % m;
for(int j = 0;j < 11;j++){
s[j][t]++;
t = t * 10 % m;
}
}
LL res = 0;
for(int i = 0;i < n;i++){
LL t = a[i] % m;
int len = to_string(a[i]).size();
res += s[len][(m - t) % m];
LL r = t;
while(len--)r = r * 10 % m;
if(r == (m - t) % m)res--; //重复
}
printf("%11d\n",res);
return 0;
}
网络分析 (难题)
某一节点发信息会传给相邻节点,直到所有节点都直接或间接收到消息
输入
n m (节点数,操作数量)
接下来m行,每三行表示一个操作 (操作1.加一条边 2.给某个联通块中的每个点加上t)
输入:
4 8
1 1 2
2 1 10
2 3 5
1 4 1
2 2 2
1 1 2
1 2 4
2 2 1
并查集基本操作:维护连通性
#include<iostream>
#include<cstring>
#include<algorithm>
const int N = 10010;
int n,m;
int p[N],d[N];
int find(int x) {
if(p[x] == x || p[p[x]] == p[x])return p[x];
int r = find(p[x]);
d[x] += d[p[x]];
p[x] = r;
return r;
}
int test_06() {
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)p[i] = i;
while(m--) {
int t,a,b;
scanf("%d%d%d",&t,&a,&b);
if(t == 1) {
a = find(a),b= find(b);
d[a] -= d[b];
p[a] = b;
}
else {
a = find(a);
d[a] += b;
}
for(int i = 1; i <= n; i++) {
if(i == find(i) )printf("%d",d[i]);
else {
printf("%d",d[i] + d[find(i)]);
puts("");
}
}
}
return 0;
}
int main() {
test_06();
return 0;
}