文章目录
- 知识点
- 回顾
- 并查集
- 朴素并查集
- 带权并查集(size并查集)
- 素数判定(Miller-Rabin算法模板)
- 逆元
- 素数筛法
- 埃氏筛 (省空间)
- 欧拉筛(速度快)
- 图论
- dfs
- bfs(更好)
- 反向图
- dfs解法
- bfs解法
- 博弈论
- 离散化
- 去重
- 最小生成树
- 解题集
- 亲戚
- 并查集
- Ubiquitous Religions
- 朋友
- 银河英雄传说
- 寻找素数对
- Max Factor
- How many prime numbers
- A/B
- 线性筛素数
- 分数
- 选数
- 素数个数
- 炸铁路
- 图的遍历
- 图的遍历(简单版)
- 悼念512汶川大地震遇难同胞——选拔志愿者
- Being a Good Boy in Spring Festival
知识点
回顾
"+\n"[i == g.size() - 1]
a>b?a:b
for(auto x:s)
多行输出
#include <iostream>
using namespace std;
int main()
{
cout << R"(
####
#**#
#**#
####
)"
return 0;
}
void buddha_bless(){
cout << R"(
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永无BUG 永不修改 //
)";
}
并查集
朴素并查集
- 路径压缩
- 按秩合并
例题P1551
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> p(5010);//我的父亲是谁
//初始化
void init(int n){
for (int i = 1; i <= n; i++)
{
p[i] = i;//自己是自己的监护人
}
}
//找自己最上面的祖宗 近乎O(1)
int find(int x){
/*
if(x != p[x]){
return find(p[x]);//上面还有人
}
else{
return x;//自己就是自己的监护人,即祖宗
}
*/
if(x != p[x]){
p[x] = find(p[x]);//路径压缩[better!]
}
return p[x];
}
//合并
void merge(int u,int v){
int pu = find(u);
int pv = find(v);
if(pu != pv){
p[pu] = pv;//新爹,合并完成
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int n, m, q;
cin >> n >> m >> q;
init(n);
while (m--){
int u, v;
cin >> u >> v;
merge(u, v);//有亲戚关系的人合并
}
while(q--){
int a, b;
cin >> a >> b;
if(find(a) == find(b)){
cout << "Yes" << endl;
}
else{
cout << "No" << endl;
}
}
return 0;
}
带权并查集(size并查集)
- size并查集:查询数组的大小权值
例题P1551
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> p(5010);//我的父亲是谁
vector<int> size(5010);//集合大小
//初始化
void init(int n){
for (int i = 1; i <= n; i++)
{
p[i] = i;//自己是自己的监护人
size[i] = 1;
}
}
//找自己最上面的祖宗 近乎O(1)
int find(int x){
if(x != p[x]){
p[x] = find(p[x]);//路径压缩[better!]
}
return p[x];
}
//合并
void merge(int u,int v){
int pu = find(u);
int pv = find(v);
if(pu!=pv){
size[pv] += size[pu];
p[pu] = pv;
}
}
void solve(){
int u,v;
merge(u,v); // 将u所在集合并到v所在集合
siz[find(u)]; // 查询u所在集合的大小
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int n, m, q;
cin >> n >> m >> q;
init(n);
while (m--){
int u, v;
cin >> u >> v;
merge(u, v);//有亲戚关系的人合并
}
while(q--){
int a, b;
cin >> a >> b;
if(find(a) == find(b)){
cout << "Yes" << endl;
}
else{
cout << "No" << endl;
}
}
return 0;
}
素数判定(Miller-Rabin算法模板)
- 大素数判定:随机一些数去试除n
- 费马小定理:如果n是素数,那么 a n − 1 m o d n = 1 a^{n-1} mod\ n = 1 an−1mod n=1
- 逆命题:如果 a n − 1 m o d n = 1 a^{n-1} mod\ n = 1 an−1mod n=1,那么n就是素数(不一定成立)
- 如果 a n − 1 m o d n ! = 1 a^{n-1} mod\ n != 1 an−1mod n!=1,那么n肯定不是素数(成立)
- 随机多个a,计算 a n − 1 m o d n a^{n-1} mod\ n an−1mod n,都等于1, n n n 大概率是素数
(可惜的是有很小很小很小的一部分合数,无论a取什么值他都能通过测试,怎么排除这部分合数?)
- 二次探测定理
推论:方程 x 2 m o d n = 1 x^2 mod\ n = 1 x2mod n=1,如果 x x x有 x = 1 x=1 x=1和 x = n − 1 x=n-1 x=n−1以外的解, n n n肯定不是素数。
Miller-Rabin算法模板
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull; // 自然溢出
typedef pair<int,int> PII;
const int S = 50; // 测试数
// 快速乘,防止乘法溢出longlong
ll mult_mod(ll u,ll v,ll p){
return (u * v - ll((long double)u * v / p) * p + p) % p;
}
// 快速幂
ll fast_pow(ll x,ll y,ll mod){
ll res = 1;
x %= mod;
while(y){
if(y & 1) res = mult_mod(res,x,mod);
x = mult_mod(x,x,mod);
y >>= 1;
}
return res;
}
bool witness(ll a,ll n){
ll u = n - 1;
int t = 0;
// 计算t,u,对n-1一直除2,直到是奇数,剩下的奇数是u,除2次数是t
while(u & 1 == 0){u = u >> 1,t++;}
ll x1,x2;
x1 = fast_pow(a,u,n); // 计算a^u
// 做t次平方
for(int i = 1;i <= t;i++){
x2 = fast_pow(x1,2,n); // 平方后的结果
// 二次探测定理
if(x2 == 1 && x1 != 1 && x1 != n - 1) return true; // 必然是合数
x1 = x2;
}
// 最后x1=a^n-1,费马小定理
if(x1 != 1) return true; // 必然是合数
return false;
}
int miller_rabin(ll n){
if(n < 2) return 0; // 小于2是合数
if(n == 2) return 1; // 2是质数
if(n % 2 == 0) return 0; // 偶数是合数
// 剩下做测试
for(int i = 0;i < S;i++){
ll a = rand() % (n - 1) + 1; // 随机一个a
if(witness(a,n)) return 0; // 测试
}
return 1;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout << fixed << setprecision(6);
ll n;
cin >> n;
if(miller_rabin(n)){
cout << "P" << endl;
}
else{
cout << "not P" << endl;
}
return 0;
}
逆元
通俗的讲,逆元可以看做一个数的倒数的整数形式,但是一个数的逆元在不同的( m o d ( n ) mod(n) mod(n))意义下是不一样的。
- a × x ≡ 1 m o d ( n ) a\times x\equiv1\quad\mathrm{mod~}(n) a×x≡1mod (n) → a × 1 a ≡ 1 m o d ( n ) a\times\frac1a\equiv1\quad\mathrm{mod~}(n) a×a1≡1mod (n)
这个方程便是逆元的真正定义,
x
x
x的解即代表
a
a
a在
m
o
d
n
~mod~n~
mod n 意义下的逆元,通俗的讲:此时的
x
x
x就相当于
a
a
a的倒数,这样
a
×
x
a×x
a×x便会等于1,在
m
o
d
n
~mod~n~
mod n 意义下余数必定为一。当然这个式子要建立在
a
a
a与
n
n
n互质
的基础上!
可是逆元有什么用呢?直接用倒数不行吗?这是因为我们发现一个分数
m
o
d
~mod~
mod 一个整数时是不能直接模运算的,但是可以进行乘法运算,我们就要用到逆元(一个数倒数的整数形式)
就像:
a
b
m
o
d
(
n
)
≠
a
m
o
d
n
b
m
o
d
n
m
o
d
(
n
)
\quad\frac ab\quad\mathrm{mod~}(n)\neq\frac{a\mathrm{~mod~}n}{b\mathrm{~mod~}n}\quad\mathrm{mod~}(n)
bamod (n)=b mod na mod nmod (n)
但是:
a
b
m
o
d
(
n
)
=
a
×
b
−
1
m
o
d
(
n
)
\quad\frac ab\quad\mathrm{mod~}(n)=a\times b^{-1}\quad\mathrm{mod~}(n)
bamod (n)=a×b−1mod (n)
所以当除运算碰上我们的模运算时,我们就需要
m
o
d
模
数
~mod~模数
mod 模数意义下的逆元了!
掌握以下知识点即可
a
b
m
o
d
(
n
)
=
a
×
b
−
1
m
o
d
(
n
)
=
(
(
a
m
o
d
(
n
)
)
×
f
a
s
t
P
o
w
e
r
(
b
,
m
o
d
(
n
)
−
2
,
m
o
d
(
n
)
)
)
m
o
d
(
n
)
\quad\frac ab\quad\mathrm{mod~}(n)=a\times b^{-1}\quad\mathrm{mod~}(n) \\=((a\quad\mathrm{mod~}(n))\times fastPower(b, mod(n) - 2, mod(n)))\quad\mathrm{mod~}(n)
bamod (n)=a×b−1mod (n)=((amod (n))×fastPower(b,mod(n)−2,mod(n)))mod (n)
(a / b) % mod;
(a % mod * b ^ -1 % mod) % mod;
ans = ((a % mod) * fastPower(b, mod - 2, mod)) % mod;
详细解释
用逆计算组合数取模
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
#define endl '\n'
const ll mod = 998244353;
ll fpow(ll x,ll y,ll mod){
ll res = 1;
x %= mod;
while(y){
if(y & 1) res = (res * x) % mod;
x = (x * x) %mod;
y >>= 1;
}
return res;
}
ll inv(ll x){
ll res = fpow(x,mod - 2,mod);
return res;
}
ll fac[100005]; // 阶乘 fac[i] = i!
ll invfac[100005]; // 阶乘的逆
void init(){
fac[0] = 1;
invfac[0] = 1;
for(int i = 1;i <= 100000;i++){
fac[i] = (fac[i - 1] * i) % mod;
invfac[i] = (invfac[i - 1] * fpow(i,mod - 2,mod)) % mod;
}
}
ll C(ll a,ll b){
if(a < b){
return 0;
}
ll res = (((fac[a] * invfac[b] ) % mod)* invfac[a - b]) % mod;
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init();
ll a,b;
cin >> a >> b;
ll res = C(a,b);
cout << res << endl;
return 0;
}
素数筛法
引入
如果我们想要知道小于等于 n 有多少个素数呢?
一个自然的想法是对于小于等于 n 的每个数进行一次质数检验。这种暴力的做法显然不能达到最优复杂度。
埃氏筛 (省空间)
过程
考虑这样一件事情:对于任意一个大于 1 的正整数 n,那么它的 x 倍就是合数(x > 1)。利用这个结论,我们可以避免很多次不必要的检测。
如果我们从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是素数了。
实现
vector<int> prime;
bool is_prime[N];
void Eratosthenes(int n) {
is_prime[0] = is_prime[1] = false;
for (int i = 2; i <= n; ++i) is_prime[i] = true;
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
prime.push_back(i);
if ((long long)i * i > n) continue;
for (int j = i * i; j <= n; j += i)
// 因为从 2 到 i - 1 的倍数我们之前筛过了,这里直接从 i
// 的倍数开始,提高了运行速度
is_prime[j] = false; // 是 i 的倍数的均不是素数
}
}
}
以上为 Eratosthenes 筛法(埃拉托斯特尼筛法,简称埃氏筛法),时间复杂度是
O
(
n
log
log
n
)
O(n\log\log n)
O(nloglogn)。
样例
【模板】线性筛素数
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
const int N = 1e8;
int prime[N + 5]; // 存放所有素数,下标从0开始
bool vit[N + 5]; // 是否被筛掉
int k = 0; // 统计素数个数
// 埃氏筛
void E_sieve(int n){
// 初始化
k = 0;
for (int i = 0; i <= n; i++){
vit[i] = 0;
}
//试除法
for (int i = 2; i * i <= n; i++){
if (vit[i] == 0){
for (int j = i * i; j <= n; j += i){
vit[j] = 1;
}
}
}
for (int i = 2; i <= n; i++){
if (vit[i] == 0){
prime[k++] = i;
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n,q;
cin>>n>>q;
E_sieve(n);
while(q--){
int x;
cin>>x;
cout << prime[x - 1] << endl;
}
}
欧拉筛(速度快)
**筛法求欧拉函数 **
注意到在线性筛中,每一个合数都是被最小的质因子筛掉。比如设
p
1
p_{1}
p1 是
n
n
n 的最小质因子,
n
′
=
n
p
1
n^{\prime}=\frac{n}{p_{1}}
n′=p1n ,那么线性筛的过程中
n
n
n 通过
n
′
×
p
1
n^{\prime} \times p_{1}
n′×p1筛掉。
观察线性筛的过程,我们还需要处理两个部分,下面对
n
′
m
o
d
p
1
n^{\prime} \bmod p_{1}
n′modp1 分情况讨论。
如果
n
′
m
o
d
p
1
=
0
n^{\prime} \bmod p_{1}=0
n′modp1=0 ,那么
n
′
n^{\prime}
n′ 包含了
n
n
n 的所有质因子。
φ
(
n
)
=
n
×
∏
i
=
1
s
p
i
−
1
p
i
=
p
1
×
n
′
×
∏
i
=
1
s
p
i
−
1
p
i
=
p
1
×
φ
(
n
′
)
\begin{aligned}\varphi(n) & =n \times \prod_{i=1}^{s} \frac{p_{i}-1}{p_{i}} \\& =p_{1} \times n^{\prime} \times \prod_{i=1}^{s} \frac{p_{i}-1}{p_{i}} \\& =p_{1} \times \varphi\left(n^{\prime}\right)\end{aligned}
φ(n)=n×i=1∏spipi−1=p1×n′×i=1∏spipi−1=p1×φ(n′)
那如果
n
′
m
o
d
p
1
≠
0
n^{\prime} \bmod p_{1} \neq 0
n′modp1=0 呢,这时
n
′
和
n^{\prime} 和
n′和 p_{1} $ 是互质的,根据欧拉函数性质,我们有:
φ
(
n
)
=
φ
(
p
1
)
×
φ
(
n
′
)
=
(
p
1
−
1
)
×
φ
(
n
′
)
\begin{aligned}\varphi(n) \ =\varphi\left(p_{1}\right) \times \varphi\left(n^{\prime}\right) \\ =\left(p_{1}-1\right) \times \varphi\left(n^{\prime}\right)\end{aligned}
φ(n) =φ(p1)×φ(n′)=(p1−1)×φ(n′)
实现
vector<int> pri;
bool not_prime[N];
int phi[N];
void pre(int n) {
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!not_prime[i]) {
pri.push_back(i);
phi[i] = i - 1;
}
for (int pri_j : pri) {
if (i * pri_j > n) break;
not_prime[i * pri_j] = true;
if (i % pri_j == 0) {
phi[i * pri_j] = phi[i] * pri_j;
break;
}
phi[i * pri_j] = phi[i] * phi[pri_j];
}
}
}
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
const int N = 1e8;
int prime[N + 5]; // 存放所有素数,下标从0开始
bool vit[N + 5]; // 是否被筛掉
int k = 0; // 统计素数个数
// 欧拉筛 O(n)
// 让每一个数只被自己最小的质因子筛掉
void E_sieve(int n){
// 初始化
k = 0;
for (int i = 0; i <= n; i++){
vit[i] = 0;
}
// 做筛子的数 每一个数
for (int i = 2; i <= n; i++){
// 是素数直接存入
if (vit[i] == 0){
prime[k++] = i;
}
// 枚举的倍数乘已经有的素数,这个素数要作为最小质因子
for (int j = 0; j < k; j++){
// 超过n退出
if (i * prime[j] > n){
break;
}
vit[i * prime[j]] = 1; // 合数筛掉
// 倍数里有小的质因子,后面更大的质因子就不用看了
if (i % prime[j] == 0) {
break;
}
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
E_sieve(n);
while (q--)
{
int x;
cin >> x;
cout << prime[x - 1] << endl;
}
}
图论
dfs
#include<bits/stdc++.h>
using namespace std;
vector<int> g[4];
int vis[1000];
int u, v;
bool dfs(int x){
if(x==v)
return true;
vis[x] = 1;
bool ok = 0;
for (int i = 0; i < g[x].size();i++){
if(vis[g[x][i]])
continue;
if (dfs(g[x][i]) == true){
ok = true;
}
}
return ok;
}
int main(){
g[1].push_back(2);
g[1].push_back(3);
g[2].push_back(3);
cin >> u >> v;
cout << (dfs(u) ? "YES" : "NO");
}
bfs(更好)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> g[1001];
int n, m,vis[1001];
int bfs(int u){
for (int i = 1; i <= n;i++){
vis[i] = 0;
}//清空标记
queue<int> q;
q.push(u);//推入初始点
vis[u] = 1;
int mx = u;
while(q.size()){
int t = q.front();//拿出一个点
q.pop();
for(auto i:g[t]){//遍历所有边
if(vis[i]==1)//如果已经推进去了就跳过
continue;
vis[i] = 1;//标记
mx = max(mx, i);
q.push(i);//并推入
}
}
return mx;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
cin >> n >> m;
for (int i = 1; i <= m;i++){
int x, y;
cin >> x >> y;
g[x].push_back(y); // 有向图存数
// g[y].push_back(x); // 无向图存数
}
for (int i = 1; i <= n;i++){
cout << bfs(i) << " \n"[i == n];
}
return 0;
}
反向图
dfs解法
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> g[100010];
int n, m,vis[100010];
void dfs(int x,int d){
if(vis[x]) return;//如果已经被探过了就返回
vis[x] = d;
for (int i = 0; i < g[x].size();i++){
dfs(g[x][i], d);
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
cin >> n >> m;
for (int i = 1; i <= m;i++){
int x, y;
cin >> x >> y;
g[y].push_back(x); // 反向图
}
for (int i = n; i >=1;i--){//反向探
dfs(i,i);
}
for (int i = 1; i <= n;i++){
cout << vis[i] << " \n"[i == n];
}
return 0;
}
bfs解法
#include<bits/stdc++.h>
using namespace std;
vector<int>a[100001];
int v[100001],n,m;
void bfs(int u){
queue<int>q;
q.push(u);//推入起始点
if(v[u]==0)
v[u]=u;
while(q.size()){
int t=q.front();
q.pop();
for(auto i : a[t]){//遍历所以边
if(v[i]!=0) continue;
v[i]=u;
q.push(i);
}
}
}
int main()
{
for(int i=1;i<=n;i++){
v[i]=0;
}
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
a[y].push_back(x);
}
for(int i=n;i>=1;i--){
bfs(i);
}
for(int i=1;i<=n;i++){
cout<<v[i]<<" ";
}
}
博弈论
若a == b
,则对面怎么拿我就怎么拿,即必胜
可以推导出 a^b == 0
为必输态
必胜态在一定条件下可以转换成必输态
将必输态留给对面则必胜
若 a^b^c^d == z 且 z>0
一定存在一个x
使得 (a-x)^b^c^d == 0 //必输态
离散化
vector<int> g;
for (int i = 1; i <= n; i++){
g.push_back(a[i]);
}
sort(g.begin(), g.end());
g.erase(unique(g.begin(), g.end()), g.end());//去重
[ 1, 3, 4 ]
for (int i = 1; i <= n; i++){
a[i] = lower bound(g, begin(), g.end(), a[i]) - g.begin() + 1;
cout << a[i] << " \n"[i == n];
}
去重
g.erase(unique(g.begin(),g.end()),g.end());
//去重
在STL中unique函数是一个去重函数, unique的功能是去除相邻的重复元素(只保留一个),其实它并不真正把重复的元素删除,是把重复的元素移到后面去了,然后依然保存到了原数组中,然后 返回去重后最后一个元素的地址,因为unique去除的是相邻的重复元素,所以一般用之前都会要排一下序。
sort,unique和erase的联合使用,可以将一个有重复元素的数组的重复元素去除,从而转化成一个无重复元素的数组
end_unique=unique(result.begin(),result.end());
result.erase(end_unique,result.end());
由于 end_unique返回去重后最后一个元素的位置,而重复的元素都被移动到后面去了,所以要将从去重后最后一个元素的地址 到 原数组最后一个地址 这些地址中的元素去掉,从而得到无重复元素的数组lower_bound && upper_bound
内置函数二分
O
(
l
o
g
n
)
O(logn)
O(logn)
lower_bound(l,r,x) // 在l~r中找到第一个大于等于x的下标
// [1 2 3 4 5 6] 需要升序
upper_bound(l,r,x) // 在l~r中找到第一个大于x的下标
最小生成树
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct node{
int u, v, w;
}p[N];
int f[N];
int find(int x){
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
void marge(int x,int y){
x = find(x);
y = find(y);
f[x] = y;
}
int main(){
int n, m;
cin >> n >> m;
for(int i=1;i<=n;i++){
f[i] = i;
}
for(int i=1;i<=m;i++){
cin >> p[i].u >> p[i].v >> p[i].w;
}
sort(p + 1, p + 1 + m, [&](node x,node y){
return x.w < y.w;
});
int ans = 0, sum = 0;
for(int i=1;i<=m;i++){
int u = p[i].u;
int v = p[i].v;
int w = p[i].w;
if(find(u) != find(v)){
ans += w;
sum += 1;
marge(u, v);
}
}
if(sum != n - 1) cout << "orz\n";
else cout << ans << '\n';
}
解题集
亲戚
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> p(5010);//我的父亲是谁
//初始化
void init(int n){
for (int i = 1; i <= n; i++)
{
p[i] = i;//自己是自己的监护人
}
}
//找自己最上面的祖宗 近乎O(1)
int find(int x){
/*
if(x != p[x]){
return find(p[x]);//上面还有人
}
else{
return x;//自己就是自己的监护人,即祖宗
}
*/
if(x != p[x]){
p[x] = find(p[x]);//路径压缩[better!]
}
return p[x];
}
//合并
void merge(int u,int v){
int pu = find(u);
int pv = find(v);
if(pu != pv){
p[pu] = pv;//新爹,合并完成
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int n, m, q;
cin >> n >> m >> q;
init(n);
while (m--){
int u, v;
cin >> u >> v;
merge(u, v);//有亲戚关系的人合并
}
while(q--){
int a, b;
cin >> a >> b;
if(find(a) == find(b)){
cout << "Yes" << endl;
}
else{
cout << "No" << endl;
}
}
return 0;
}
并查集
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> p(N);//我的父亲是谁
//初始化
void init(int n){
for (int i = 1; i <= n; i++)
{
p[i] = i;//自己是自己的监护人
}
}
//找自己最上面的祖宗 近乎O(1)
int find(int x){
if(x != p[x]){
p[x] = find(p[x]);//路径压缩[better!]
}
return p[x];
}
//合并
void merge(int u,int v){
int pu = find(u);
int pv = find(v);
if(pu != pv){
p[pu] = pv;//新爹,合并完成
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int n, m;
cin >> n >> m ;
init(n);
while (m--){
int z, x, y;
cin >> z >> x >> y;
if(z==1){
merge(x, y);//有亲戚关系的人合并
}
if(z==2){
if(find(x) == find(y)){
cout << "Y" << endl;
}
else{
cout << "N" << endl;
}
}
}
return 0;
}
Ubiquitous Religions
#include <vector>
#include <iostream>
#include <cstdio>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> p(N);//我的父亲是谁
vector<int> siz(N);//集合大小
//初始化
void init(int n){
for (int i = 1; i <= n; i++)
{
p[i] = i;//自己是自己的监护人
siz[i] = 1;
}
}
//找自己最上面的祖宗 近乎O(1)
int find(int x){
if(x != p[x]){
p[x] = find(p[x]);//路径压缩[better!]
}
return p[x];
}
//合并
void merge(int u,int v){
int pu = find(u);
int pv = find(v);
if(pu!=pv){
siz[pv] += siz[pu];
p[pu] = pv;
}
}
signed main(){
int n, m;
int cnt = 1,sum=0;
while(scanf("%lld %lld", &n, &m)&&n!=0&&m!=0){
init(n);
while(m--){
int a, b;
scanf("%lld %lld", &a, &b);
merge(a, b);
}
sum = 0;
for (int i = 1; i <= n; i++)
if (p[i] == i)
sum++;
printf("Case %lld: %lld\n", cnt, sum);
cnt++;
}
return 0;
}
朋友
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> p(N);//我的父亲是谁
vector<int> sise(N);//集合大小
//初始化
void init(int n){
for (int i = 1; i <= n; i++)
{
p[i] = i;//自己是自己的监护人
sise[i] = 1;
}
}
//找自己最上面的祖宗 近乎O(1)
int find(int x){
if(x != p[x]){
p[x] = find(p[x]);//路径压缩[better!]
}
return p[x];
}
//合并
void merge(int u,int v){
int pu = find(u);
int pv = find(v);
if(pu!=pv){
sise[pv] += sise[pu];
p[pu] = pv;
}
}
signed main(){
int n, m, p, q;
scanf("%lld %lld %lld %lld", &n, &m, &p, &q);
init(n);
while (p--){
int u, v;
cin >> u >> v;
merge(u, v);//有亲戚关系的人合并
}
int x = sise[find(1)];
init(m);
while (q--){
int u, v;
cin >> u >> v;
u *= -1, v *= -1;
merge(u, v);//有亲戚关系的人合并
}
int y = sise[find(1)];
int sum = min(x, y);
printf("%lld\n", sum);
return 0;
}
银河英雄传说
#include<bits/stdc++.h>
using namespace std;
// #define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
int p[30005]; //我的父亲是谁
int dis[30005]; //我到祖宗的距离
int siz[30005];
//初始化n个人(独立的个体)
void init(int n){
for(int i=1;i<=n;i++){
p[i] = i; //自己是自己的监护人
dis[i] = 0;
siz[i] = 1; //自己是第一
}
}
//查询(找自己最上面的祖宗)
int find(int x){
if(x != p[x]){
int root = find(p[x]);
dis[x] += dis[p[x]];
p[x] = root;
}
return p[x];
}
//合并有亲戚关系的
void merge(int u, int v){
int pu = find(u); //找u的爹
int pv = find(v); //找v的爹
if(pu != pv){
dis[pu] = siz[pv];
siz[pv] += siz[pu];
p[pu] = pv;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cout << fixed << setprecision(6);
int q;
cin >> q;
init(30000);
while(q--){
char op;
int i, j;
cin >> op >> i >> j;
if(op == 'M'){
merge(i, j);
}else if(op == 'C'){
if(find(i) == find(j)){ //同为一个祖宗
int ans = max(0, abs(dis[i] - dis[j])- 1 );
cout << ans << endl;
} else{
cout << "-1\n";
}
}
}
return 0;
}
寻找素数对
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
bool miller_rabin(int x) {
if (x < 2)
{
return false;
}
//试除法
for (int i = 2; i <= x/i; i++)
{
if (x % i == 0) {
return false;
}
}
return true;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout << fixed << setprecision(6);
int n;
while(cin >> n){
int x1=0, x2=0, cnt=0x3f3f3f3f;
for (int i = 1; i < n;i++){
if(miller_rabin(i)&&miller_rabin(n-i)){
if(abs(n-2*i)<cnt){
x1 = min(i, n - i);
x2 = max(i, n - i);
cnt=abs(n - 2 * i);
}
}
}
cout << x1 << " " << x2 << endl;
}
return 0;
}
Max Factor
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> su(20010,0);
typedef long long ll;
typedef unsigned long long ull; // 自然溢出
typedef pair<int,int> PII;
const int S = 50; // 测试数
// 快速乘,防止乘法溢出longlong
ll mult_mod(ll u,ll v,ll p){
return (u * v - ll((double)u * v / p) * p + p) % p;
}
// 快速幂
ll fast_pow(ll x,ll y,ll mod){
ll res = 1;
x %= mod;
while(y){
if(y & 1) res = mult_mod(res,x,mod);
x = mult_mod(x,x,mod);
y >>= 1;
}
return res;
}
bool witness(ll a,ll n){
ll u = n - 1;
int t = 0;
// 计算t,u,对n-1一直除2,直到是奇数,剩下的奇数是u,除2次数是t
while(u & 1 == 0){u = u >> 1,t++;}
ll x1,x2;
x1 = fast_pow(a,u,n); // 计算a^u
// 做t次平方
for(int i = 1;i <= t;i++){
x2 = fast_pow(x1,2,n); // 平方后的结果
// 二次探测定理
if(x2 == 1 && x1 != 1 && x1 != n - 1) return true; // 必然是合数
x1 = x2;
}
// 最后x1=a^n-1,费马小定理
if(x1 != 1) return true; // 必然是合数
return false;
}
int miller_rabin(ll n){
if(n < 2) return 0; // 小于2是合数
if(n == 2) return 1; // 2是质数
if(n % 2 == 0) return 0; // 偶数是合数
// 剩下做测试
for(int i = 0;i < S;i++){
ll a = rand() % (n - 1) + 1; // 随机一个a
if(witness(a,n)) return 0; // 测试
}
return 1;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
for (int i = 0; i <= 20000; i++)
{
if(miller_rabin(i)){
su[i] = 1;
}
}
int n;
while(cin >> n){
int max1 = 0,max2=0,m=0;
int x;
for (int i = 1; i <= n; i++){
cin >> x;
for (int j = x-1; j > 1;j--){
if(su[j]==1&&x%j==0){//是质因子
max1 = j;
if(max1>max2){
max2 = max1;
m = x;
}
break;
}
}
}
cout << m << endl;
}
return 0;
}
How many prime numbers
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
bool isPrime(int x) {
if (x < 2)
{
return false;
}
//试除法
for (int i = 2; i <= x/i; i++)
{
if (x % i == 0) {
return false;
}
}
return true;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout << fixed << setprecision(6);
int n,x;
while(cin >> n){
int cnt = 0;
for (int i = 1; i <= n; i++){
cin >> x;
if(isPrime(x)){
cnt++;
}
}
cout << cnt << endl;
}
return 0;
}
A/B
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
#define mod1 9973
int fastPower(int a, int b, int mod/* 取模 */) {
int result = 1; // 初始化结果为1
a = a % mod; // 将a对m取模
while (b > 0) {
// 如果b的当前位是1,则将当前的a乘到result上
if (b & 1) { // 等价于 if (b % 2 == 1)
result *= a;
result %= mod;
}
// 将a平方,准备处理下一位
a *= a;
a %= mod;
// b右移一位
b >>= 1; // 等价于 b = b / 2;
}
return result;
}
void solve(){
int a, b;
cin >> a >> b;
// (a / b) % mod;
// (a % mod * b ^ -1 % mod) % mod;
int ans = ((a % mod1) * fastPower(b, mod1 - 2, mod1)) % mod1;
cout << ans << endl;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
int t;
cin >> t;
while(t--){
solve();
}
return 0;
}
线性筛素数
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
const int N = 1e8;
int prime[N + 5]; // 存放所有素数,下标从0开始
int vit[N + 5]; // 是否被筛掉
int k = 0; // 统计素数个数
/* // 埃氏筛
void E_sieve(int n){
// 初始化
k = 0;
for (int i = 0; i <= n; i++){
vit[i] = 0;
}
//
for (int i = 2; i * i <= n; i++){
if (vit[i] == 0){
for (int j = i * i; j <= n; j += i){
vit[j] = 1;
}
}
}
for (int i = 2; i <= n; i++){
if (vit[i] == 0){
prime[k++] = i;
}
}
}
*/
// 欧拉筛 O(n)
// 让每一个数只被自己最小的质因子筛掉
void E_sieve(int n){
// 初始化
k = 0;
for (int i = 0; i <= n; i++){
vit[i] = 0;
}
// 做筛子的数 每一个数
for (int i = 2; i <= n; i++){
// 是素数直接存入
if (vit[i] == 0){
prime[k++] = i;
}
// 枚举的倍数乘已经有的素数,这个素数要作为最质因子
for (int j = 0; j < k; j++){
// 超过n退出
if (i * prime[j] > n){
break;
}
vit[i * prime[j]] = 1; // 合数筛掉
// 倍数里有小的质因子,后面更大的质因子就不用看了
if (i % prime[j] == 0){
break;
}
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
E_sieve(n);
while (q--)
{
int x;
cin >> x;
cout << prime[x - 1] << endl;
}
}
分数
#include<bits/stdc++.h>
using namespace std;
const int n=2e6;
int p[n+5];
int v[n+5];
int ans[n+5];
int k=0;
void Euler_sieve(int n){
for(int i=0;i<=n;i++){
v[i]=0;
}
for(int i=2;i<=n;i++){
if(v[i]==0) p[k++]=i;
for(int j=0;j<k;j++){
if(i*p[j]>n) break;
v[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
}
void init(){
int now=0;
for(int i=1;i<=n;i++){
if(!v[i]) now=i;
ans[i]=now;
}
}
int main()
{
int t;
cin>>t;
Euler_sieve(n);
init();
while(t--){
int x;
cin>>x;
cout<<ans[x]<<endl;
}
}
选数
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 5e6 + 10;
const double eps =1e-4;
vector<int> s;
int n, k, num = 0;
typedef long long ll;
typedef unsigned long long ull; // 自然溢出
typedef pair<int,int> PII;
const int S = 50; // 测试数
// 快速乘,防止乘法溢出longlong
ll mult_mod(ll u,ll v,ll p){
return (u * v - ll((/*long*/ double)u * v / p) * p + p) % p;
}
// 快速幂
ll fast_pow(ll x,ll y,ll mod){
ll res = 1;
x %= mod;
while(y){
if(y & 1) res = mult_mod(res,x,mod);
x = mult_mod(x,x,mod);
y >>= 1;
}
return res;
}
bool witness(ll a,ll n){
ll u = n - 1;
int t = 0;
// 计算t,u,对n-1一直除2,直到是奇数,剩下的奇数是u,除2次数是t
while(u & 1 == 0){u = u >> 1,t++;}
ll x1,x2;
x1 = fast_pow(a,u,n); // 计算a^u
// 做t次平方
for(int i = 1;i <= t;i++){
x2 = fast_pow(x1,2,n); // 平方后的结果
// 二次探测定理
if(x2 == 1 && x1 != 1 && x1 != n - 1) return true; // 必然是合数
x1 = x2;
}
// 最后x1=a^n-1,费马小定理
if(x1 != 1) return true; // 必然是合数
return false;
}
int miller_rabin(ll n){
if(n < 2) return 0; // 小于2是合数
if(n == 2) return 1; // 2是质数
if(n % 2 == 0) return 0; // 偶数是合数
// 剩下做测试
for(int i = 0;i < S;i++){
ll a = rand() % (n - 1) + 1; // 随机一个a
if(witness(a,n)) return 0; // 测试
}
return 1;
}
int vis[N+10];
void dfs(vector<int> g,int cnt,int mx){
if(cnt == k){
int sum = 0;
for(int i = 0 ;i < k; i++){
sum += g[i];
}
if(miller_rabin(sum)){
num++;
}
return;
}
for(int i = 1 ; i <= n; i++){
if(vis[i] == 1 || i < mx) continue;//已经标记就跳过
g.push_back(s[i]);
vis[i] = 1;//标记
dfs(g, cnt + 1,i);
g.pop_back();
vis[i] = 0;//取消标记
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
cin >> n >> k;
s.push_back(0);
int sx = 0;
for (int i = 1; i <= n;i++){
cin >> sx;
s.push_back(sx);
}
sort(s.begin(), s.end());
dfs({}, 0, 0);
cout << num << endl;
return 0;
}
素数个数
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e8 + 5;
const double eps =1e-4;
int prime[N + 5]; // 存放所有素数,下标从0开始
bool vit[N + 5]; // 是否被筛掉
int k = 0; // 统计素数个数
void E_sieve(int n){
// 初始化
k = 0;
for (int i = 0; i <= n; i++){
vit[i] = 0;
}
//
for (int i = 2; i * i <= n; i++){
if (vit[i] == 0){
for (int j = i * i; j <= n; j += i){
vit[j] = 1;
}
}
}
for (int i = 2; i <= n; i++){
if (vit[i] == 0){
k++;
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
int n ;
while (cin >> n){
E_sieve(n);
cout << k << endl;
}
return 0;
}
炸铁路
图的遍历
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> g[100010];
int n, m,vis[100010];
void dfs(int x,int d){
if(vis[x]) return;//如果已经被探过了就返回
vis[x] = d;
for (int i = 0; i < g[x].size();i++){
dfs(g[x][i], d);
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
cin >> n >> m;
for (int i = 1; i <= m;i++){
int x, y;
cin >> x >> y;
g[y].push_back(x); // 反向图
}
for (int i = n; i >=1;i--){//反向探
dfs(i,i);
}
for (int i = 1; i <= n;i++){
cout << vis[i] << " \n"[i == n];
}
return 0;
}
图的遍历(简单版)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define fi first
#define se second
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
vector<int> g[1001];
int n, m,vis[1001];
int bfs(int u){
for (int i = 1; i <= n;i++){
vis[i] = 0;
}//清空标记
queue<int> q;
q.push(u);//推入初始点
vis[u] = 1;
int mx = u;
while(q.size()){
int t = q.front();//拿出一个点
q.pop();
for(auto i:g[t]){//遍历所有边
if(vis[i]==1)//如果已经推进去了就跳过
continue;
vis[i] = 1;//标记
mx = max(mx, i);
q.push(i);//并推入
}
}
return mx;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
cin >> n >> m;
for (int i = 1; i <= m;i++){
int x, y;
cin >> x >> y;
g[x].push_back(y); // 有向图存数
// g[y].push_back(x); // 无向图存数
}
for (int i = 1; i <= n;i++){
cout << bfs(i) << " \n"[i == n];
}
return 0;
}
悼念512汶川大地震遇难同胞——选拔志愿者
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
int n;
cin >> n;
while(n--){
int a, b;
cin >> a >> b;
if(a%(b+1)==0) cout<<"Rabbit"<<endl;
else cout<<"Grass"<<endl;
}
return 0;
}
Being a Good Boy in Spring Festival
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl "\n"
#define mod3 998244353
#define mod7 1000000007
const int N = 1e6 + 10;
const double eps =1e-4;
int a[1001000];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cout << fixed << setprecision(6);
int n;
while(cin>>n&&n){
int sum = 0;
for (int i = 1; i <= n;i++){
cin >> a[i];
sum ^= a[i];
}
int ans=0;
for (int i = 1; i <= n;i++){
if(a[i] >= (sum^a[i])){
ans++;
}
}
if (sum == 0)
ans = 0;
cout << ans << endl;
}
return 0;
}