以前用过的算法模板。

线性筛素数

给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内)

在没有读入优化的情况下,这份代码提交到洛谷上速度尚可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<cstdio>
using namespace std;

const int MAXN = 10000100;
bool composite[MAXN];
int prime[MAXN],tail;

void get_prime(int n)
{
composite[0]=true;
composite[1]=true;
for(int i=2;i<=n;i++)
{
if(!composite[i])prime[tail++]=i;
for(int j=0;j<tail&&i*prime[j]<=n;j++)
{
composite[i*prime[j]]=true;
if(!(i%prime[j]))break;
}
}
}

int main()
{
int n,t,temp;
scanf("%d%d",&n,&t);
get_prime(n);

for(int i=1;i<=t;i++){
scanf("%d",&temp);
if(composite[temp])puts("No");
else puts("Yes");
}

return 0;
}

KMP字符串匹配

KMP:一个人能走的多远不在于他在顺境时能走的多快,而在于他在逆境时多久能找到曾经的自己。

对着ryf的博客思路自己写的代码实现,直观但是效率低(会超时):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <string>
#include <vector>

int tsize, ssize;
std::vector<int> mynext;

void getNext(std::string &t){
mynext.resize(tsize);
mynext[0]=0;
for(int i=1; i<tsize; i++){
int k=mynext[i-1];
while((t[i] != t[k]) && (k!=0))k=mynext[k-1];
if(t[i] == t[k])mynext[i]=k+1;
else mynext[i]=0;
}
}

inline void output(int i){
std::cout<<i+1<<"\n";
}

void kmp(std::string &s, std::string &t, int startpos){
int i = startpos, j = 0;
ssize = s.size(), tsize = t.size();
getNext(t);
while(s[i]!=t[0]){i++;}
int icopy = i;

while(i+tsize<=ssize){
while(s[i]!=t[0] && i+tsize<ssize){i++; icopy=i;}
if(s[icopy]==t[j]){
icopy++, j++;
if(j==tsize){
output(i);
i += j-mynext[j-1];
icopy = i; j = 0;
}
}
else{
if(i+tsize>=ssize)return;
i += j-mynext[j-1];
icopy = i; j = 0;
}
}
}

int main(){
std::string s, t;
std::cin>>s>>t;
kmp(s, t, 0);

for(auto i = mynext.begin(); i != mynext.end(); i++){
std::cout<<*i<<" ";
}
return 0;
}

提交入口: https://www.luogu.com.cn/problem/P3375

9-0.gif

根据教学视频改进的可用版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <cstdio>
#include <cstring>

const int MAXL = 1e6+10;
char s[MAXL], t[MAXL];
int mynext[MAXL], ssize, tsize;

inline void output(int i){
printf("%d\n",i+1);
//题目要求下标从1开始
//仅在输出时作相应处理
}
/* 需要其他形式的KMP可以由以下代码改:
class Solution {
public:
void getNext(int* next, const string& s) {
int j = 0;
next[0] = 0;
for(int i = 1; i < s.size(); i++) {
while (j > 0 && s[i] != s[j]) {
j = next[j - 1];
}
if (s[i] == s[j]) {
j++;
}
next[i] = j;
}
}
int strStr(string haystack, string needle) {
if (needle.size() == 0) {
return 0;
}
int next[needle.size()];
getNext(next, needle);
int j = 0;
for (int i = 0; i < haystack.size(); i++) {
while(j > 0 && haystack[i] != needle[j]) {
j = next[j - 1];
}
if (haystack[i] == needle[j]) {
j++;
}
if (j == needle.size() ) {
return (i - needle.size() + 1);
}
}
return -1;
}
};
*/
void getNext(){
int j = 0;
mynext[0] = 0;
for(int i=1; i<tsize; i++){
while(j>0 && t[i]!=t[j]) j=mynext[j-1];
if(t[i]==t[j]) j++;
mynext[i] = j;
}
}

void kmpfind(){
if(!tsize){printf("寻找内容为空串!\n"); return;}
getNext();
int j = 0;
for (int i = 0; i < ssize; i++) {
while(j > 0 && s[i] != t[j]) {
j = mynext[j-1];
}
if(s[i]==t[j]) j++;
if (j==tsize) {
output(i-tsize+1);
j = mynext[j-1];
}
}
}

int main(){
scanf("%s%s",s,t);
ssize=strlen(s); tsize=strlen(t);
//本程序下标都是从0开始
kmpfind();

for(int i = 0; i<tsize; i++){
printf("%d ",mynext[i]);
}
return 0;
}

并查集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN=10010;
int uset[MAXN];

void makeset(int size){
for(int i=0;i<size;i++)uset[i]=i;
}

int find(int x){
if(x!=uset[x])uset[x]=find(uset[x]);
return uset[x];
}

void unionset(int x,int y){
x=find(x);
y=find(y);
if(x==y)return;
uset[x]=y;
}


int main()
{
int n,m; cin>>n>>m;
makeset(n+1);

int z,x,y;
while(m--)
{
scanf("%d%d%d",&z,&x,&y);
if(z==1)unionset(x,y);
else if(z==2){
if(find(x)==find(y))
printf("Y\n");
else printf("N\n");
}
}

return 0;
}

自适应辛普森

题面:https://www.luogu.com.cn/problem/P4525

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <cstdio>
#include <cmath>

double a,b,c,d,L,R;
inline double f(double x){
return (c*x+d)/(a*x+b);
}

inline double simpson(double l, double r){
double mid = l+(r-l)/2;
return (f(l)+4*f(mid)+f(r))/6.0*(r-l);
}

double asr(double l, double r, double eps, double S, int cnt){
double mid = l+(r-l)/2;
double s1 = simpson(l, mid), s2 = simpson(mid, r);
if(fabs(s1+s2-S)<=15*eps && cnt<=0)
return s1+s2+(s1+s2-S)/15.0;
return asr(l, mid, eps/2, s1, cnt-1) + asr(mid, r, eps/2, s2, cnt-1);
}

double calc(double l, double r, double eps){
return asr(l, r, eps, simpson(l,r), 12);
}

int main(){
scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&L,&R);
printf("%.6lf",calc(L,R,1e-6));
return 0;
}

Huffman树

参考:https://blog.csdn.net/weixin_43191865/article/details/97974221

一些概念:

  • 结点的带权路径长度:指的是从根结点到该结点之间的路径长度与该结点的权的乘积。
  • 树的带权路径长度为树中所有叶子结点的带权路径长度之和,通常记作WPL。例如下图所示的这颗树的带权路径长度为:WPL=7*1+5*2+2*3+4*3

9-1.png

当用n个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”。在构建哈弗曼树时,只需要遵循一个原则:权重越大的结点离树根越近。

例如:3 4 5 8 ,设最后答案为ans

首先我们选3 4,合并节点,新点权值为7,并加入原序列,ans+=(3+4)

9-2.png

然后新序列中合并5和7,新点权值为12,ans+=(5+7)

9-3.png

最后合并12和8,新节点为20,跳出循环,ans+=(12+8)

9-4.png

最后的哈夫曼树就是右边黑色的那棵树,答案就是ans。

对于某一个节点,因为其被合并之后的值给了新的节点,而新的节点合并的时候又会加上这个值,实际上是不断为答案作贡献的。

k叉哈夫曼树: 为保证其根节点可以选到k个子树,假设节点个数为n,需要满足(n-1)mod(k-1)==0的条件,若不满足,为原序列补0

例题:NOI2015荷马史诗

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <cstdio>
#include <queue>
#define MAX(a,b) ((a)>(b)?(a):(b))

typedef long long LL;
struct node{
LL w,depth;
bool operator<(const node& v)const{
if(w!=v.w) return w>v.w;
else return depth>v.depth;
}
};
std::priority_queue<node> qwq;
LL n,k,ans;

int main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++){
LL tempw;
scanf("%lld",&tempw);
qwq.push((node){tempw,1});
}

LL qwqsize = qwq.size();
while((qwqsize-1)%(k-1)){
qwq.push((node){0,1});
qwqsize++;
}

while(qwqsize>=k){
LL tw = 0,tdep = -1;
for(int i=1; i<=k; i++){
node tq = qwq.top();
qwq.pop(); qwqsize--;
tdep = MAX(tdep,tq.depth);
tw += tq.w;
}
ans += tw;
qwq.push((node){tw,tdep+1});
qwqsize++;
}

printf("%lld\n%lld",ans,qwq.top().depth-1);
return 0;
}

拓扑排序

例题:https://www.luogu.com.cn/problem/P1983

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <cstdio>
#include <queue>
#include <cstring>

const int MAXN = 1e3+10;
const int MAXM = 5e7+10; //算出来的空间花费远比这个多,windows会阻止运行,算错了?
int tot,head[MAXN],n,m,spot[MAXN],in[MAXN],ans;
struct Edge{int v,next;}edge[MAXM];
bool isspot[MAXN],has[MAXN][MAXN];

inline void addedge(int x,int y){
edge[++tot].v=y;
edge[tot].next=head[x];
head[x]=tot;
} //本题不应当采用这种存图方式,但我直接用has数组避免重构了

std::queue<int> qwq;
void toposort(){ //这里的toposort为适应题目做了改造
int cnt1 = 0, cnt2 = 0;
for(int i=1;i<=n;i++){
if(!in[i]){
qwq.push(i);
cnt1++;
}
}

while(!qwq.empty()){
int x = qwq.front();
qwq.pop(); cnt1--;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].v; in[y]--;
if(!in[y]){
qwq.push(y);
cnt2++;
}
}
if(!cnt1){ans++; cnt1=cnt2; cnt2=0;} //神之一手(雾)
}
}

int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int s; scanf("%d",&s);
memset(isspot,false,sizeof(isspot));
for(int j=1;j<=s;j++){
scanf("%d",&spot[j]);
isspot[spot[j]]=true;
}
for(int j=spot[1];j<=spot[s];j++){ //连续站点枚举
if(!isspot[j]){
for(int k=1;k<=s;k++){
if(has[spot[k]][j])continue;
has[spot[k]][j] = true;
addedge(spot[k],j);
in[j]++;
}
}
}
}

toposort();
printf("%d",ans);
return 0;
}

最小生成树

给出一个无向图,求出最小生成树,如果该图不连通,则输出orz

输入格式:

第一行包含两个整数N、M,表示该图共有N个结点和M条无向边。(N<=5000,M<=200000)

接下来M行每行包含三个整数Xi、Yi、Zi,表示有一条长度为Zi的无向边连接结点Xi、Yi

输出格式:

输出包含一个数,即最小生成树的各边的长度之和;如果该图不连通则输出orz

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//kruscal
#include<cstdio>
#include<algorithm>
using namespace std;

const int MAXN = 5000+10;
const int MAXM = 200000+10;
struct Edge{int u,v,w;}edge[MAXM];
int uset[MAXN],n,m,ans;

int find(int x){
if(uset[x]!=x)uset[x]=find(uset[x]);
return uset[x];
}

void unionset(int x,int y){
x=find(x);
y=find(y);
if(x==y)return;
uset[x]=y;
}

bool cmp(Edge x,Edge y){return x.w<y.w;}

int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
edge[i].u=x;
edge[i].v=y;
edge[i].w=z;
}

sort(edge+1,edge+1+m,cmp);
for(int i=0;i<=n;i++)uset[i]=i;

int cnt=0;
for(int i=1;i<=m;i++)
{
int x=find(edge[i].u);
int y=find(edge[i].v);
if(x==y)continue;
ans+=edge[i].w;
unionset(x,y);
if(++cnt==n-1)break;
}

if(cnt<n-1)printf("orz");
else printf("%d",ans);
return 0;
}

单源最短路径

给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入格式:

第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

输出格式:

一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)

$SPFA$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include<bits/stdc++.h>
#define MAXM 500010
#define MAXN 10010
#define INF 2147483647
using namespace std;

int n,m,s,dis[MAXN];
bool inq[MAXN];
struct Edge{int v,w,next;}edge[MAXM];
int tot,head[MAXN];

inline void addedge(int x,int y,int z){
edge[++tot].v=y;
edge[tot].w=z;
edge[tot].next=head[x];
head[x]=tot;
}

void spfa()
{
queue<int> qwq;
for(int i=1;i<=n;i++)dis[i]=INF;

qwq.push(s);
dis[s]=0;inq[s]=true;

while(!qwq.empty())
{
int x=qwq.front();
qwq.pop(); inq[x]=false;

for(int i=head[x];i;i=edge[i].next){
int y=edge[i].v;
if(dis[y]>dis[x]+edge[i].w){
dis[y]=dis[x]+edge[i].w;
if(!inq[y]){qwq.push(y);inq[y]=true;}
}
}
}
}

int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}

spfa();

for(int i=1;i<=n;i++)
if(s==i)printf("0 ");
else printf("%d ",dis[i]);

return 0;
}

$Dijkstra$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<cstdio>
#include<queue>
#define MAXM 200010
#define MAXN 100010
using namespace std;

const int INF =2147483647;
int n,m,s,dis[MAXN];
bool done[MAXN];
int tot,head[MAXN];
struct Edge{int v,w,next;}edge[MAXM];

inline void addedge(int x,int y,int z)
{
edge[++tot].v=y;
edge[tot].w=z;
edge[tot].next=head[x];
head[x]=tot;
}

struct node{
int u,dist;
bool operator<(const node& v)const{
return dist>v.dist;
}
};

priority_queue<node> qwq;
inline void dijkstra()
{
for(int i=1;i<=n;i++)dis[i]=INF;
dis[s]=0;
qwq.push((node){s,0});

while(!qwq.empty())
{
node front=qwq.top(); qwq.pop();
int u=front.u,dist=front.dist;
if(done[u])continue;
done[u]=true;
for(int i=head[u];i;i=edge[i].next)
{
int y=edge[i].v,z=edge[i].w;
if(dis[u]+z<dis[y])
{
dis[y]=dis[u]+z;
qwq.push((node){y,dis[y]});
}
}
}
}

int main()
{
scanf("%d%d%d",&n,&m,&s);
int x,y,z;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}

dijkstra();

for(int i=1;i<=n;i++)printf("%d ",dis[i]);
return 0;
}

树状数组

已知一个数列,你需要进行下面两种操作:

1.将某一个数加上x

2.求出某区间每一个数的和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<cstdio>
#define lowbit(x) (x&(-x))
using namespace std;

const int MAXN = 500010;
int c[MAXN],n,m;

inline int sum(int i)
{
int s=0;
while(i>0){
s+=c[i];
i-=lowbit(i);
}
return s;
}

inline void update(int i,int value)
{
while(i<=n){
c[i]+=value;
i+=lowbit(i);
}
}

int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int temp;scanf("%d",&temp);
update(i,temp);
}

for(int i=1;i<=m;i++)
{
int flag,x,y;
scanf("%d%d%d",&flag,&x,&y);
if(flag==1) update(x,y);
else printf("%d\n",sum(y)-sum(x-1));
}

return 0;
}

已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数数加上x

2.求出某一个数的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include<cstdio>
#define lowbit(x) (x&(-x))
using namespace std;

const int MAXN = 500010;
int c[MAXN],n,m,pre,now;

inline int sum(int i)
{
int s=0;
while(i>0){
s+=c[i];
i-=lowbit(i);
}
return s;
}

inline void update(int i,int value)
{
while(i<=n){
c[i]+=value;
i+=lowbit(i);
}
}

int main()
{
scanf("%d%d",&n,&m);

for(int i=1;i<=n;i++)
{
scanf("%d",&now);
update(i,now-pre);
pre=now;
}

for(int i=1;i<=m;i++)
{
int flag,x;
scanf("%d%d",&flag,&x);
if(flag==1)
{
int y,k;scanf("%d%d",&y,&k);
update(x,k);update(y+1,-k);
}
else printf("%d\n",sum(x));
}

return 0;
}

最近公共祖先

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;

const int MAXN = 500010;
const int MAXM = MAXN-1;
int max0,n,m,tot,head[MAXN];
int fa[MAXN][25],dep[MAXN],s;
struct Edge{int v,next;}edge[MAXM<<1];

inline void addedge(int x,int y)
{
edge[++tot].v=y;
edge[tot].next=head[x];
head[x]=tot;
}

void lcainit(int x)
{
for(int i=1;i<=max0;i++)
if(fa[x][i-1])fa[x][i]=fa[fa[x][i-1]][i-1];
else break;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].v;
if(y!=fa[x][0]){
fa[y][0]=x;
dep[y]=dep[x]+1;
lcainit(y);
}
}
}

inline int lca(int u,int v)
{
if(dep[u]<dep[v])swap(u,v);
int delta=dep[u]-dep[v];
for(int x=0;x<=max0;x++)
if((1<<x)&delta)u=fa[u][x];
if(u==v)return u;
for(int x=max0;x>=0;x--)
if(fa[u][x]!=fa[v][x]){
u=fa[u][x];
v=fa[v][x];
}
return fa[u][0];
}

int main()
{
scanf("%d%d%d",&n,&m,&s);
max0=(int)(log(n)/log(2))+3;

for(int i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
addedge(x,y);addedge(y,x);
}

lcainit(s);

for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",lca(a,b));
}

return 0;
}

割边

提交入口:https://www.luogu.com.cn/problem/T103481

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <cstdio>
#define MIN(a,b) ((a)<(b)?(a):(b))

const int MAXN = 5e4+10;
const int MAXM = 3e5+10;
int head[MAXN],tot,n,m,num,dfn[MAXN],low[MAXN],ans;
struct Edge{int v,next;}edge[MAXM<<1];

inline void addedge(int x,int y){
edge[++tot].v=y;
edge[tot].next=head[x];
head[x]=tot;
}

void tarjan(int x, int in_edge){
dfn[x]=low[x]=++num;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].v;
if(!dfn[y]){
tarjan(y,i); //把事情当作已经完成是理解递归的关键
low[x]=MIN(low[x], low[y]);
if(dfn[x]<low[y]) ans++; //割边判定
}
else if(i!=(in_edge^1))
low[x]=MIN(low[x], dfn[y]);
//不能通过来时的对应边访问父亲,但可以通过其他重边访问父亲
}
}

int main(){
scanf("%d%d",&n,&m);
tot = 1;
for(int i=1; i<=m; i++){
int x,y; scanf("%d%d",&x,&y);
addedge(x,y); addedge(y,x);
}

for(int i=1; i<=n; i++)
if(!dfn[i]) tarjan(i,0);

printf("%d",ans);
return 0;
}

若需要输出割边:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <cstdio>
#define MIN(a,b) ((a)<(b)?(a):(b))

const int MAXN = 5e4+10;
const int MAXM = 3e5+10;
int head[MAXN],tot,n,m,num,dfn[MAXN],low[MAXN];
struct Edge{int v,next;}edge[MAXM<<1];
bool isbridge[MAXM<<1];

inline void addedge(int x,int y){
edge[++tot].v=y;
edge[tot].next=head[x];
head[x]=tot;
}

void tarjan(int x, int in_edge){
dfn[x]=low[x]=++num;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].v;
if(!dfn[y]){
tarjan(y,i); //把事情当作已经完成是理解递归的关键
low[x]=MIN(low[x], low[y]);
if(dfn[x]<low[y]){
isbridge[i]=isbridge[i^1]=true;
}
}
else if(i!=(in_edge^1))
low[x]=MIN(low[x], dfn[y]);
//不能通过来时的对应边访问父亲,但可以通过其他重边访问父亲
}
}

int main(){
scanf("%d%d",&n,&m);
tot = 1;
for(int i=1; i<=m; i++){
int x,y; scanf("%d%d",&x,&y);
addedge(x,y); addedge(y,x);
}

for(int i=1; i<=n; i++)
if(!dfn[i]) tarjan(i,0);

for(int i=2; i<tot; i+=2) //无向图
if(isbridge[i]) //i^1在前是因为edge[].v是到达的点
printf("%d %d\n",edge[i^1].v,edge[i].v);

return 0;
}

割点

提交入口: https://www.luogu.com.cn/problem/P3388

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <cstdio>
#define MIN(a,b) ((a)<(b)?(a):(b))
using namespace std;

const int MAXN = 2e4+3;
const int MAXM = 1e5+3;
int n,m,head[MAXN],tot,ans;
int num,dfn[MAXN],low[MAXN],sroot;
struct Edge{int v,next;}edge[MAXM<<1];
bool iscut[MAXN];

inline void addedge(int x, int y){
edge[++tot].v=y;
edge[tot].next=head[x];
head[x]=tot;
}

void tarjan(int x){
dfn[x]=low[x]=++num;
int mflag = 0;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].v;
if(!dfn[y]){
tarjan(y);
low[x]=MIN(low[x],low[y]);
if(low[y]>=dfn[x]){
mflag++;
if(x!=sroot || mflag>1)iscut[x]=true;
}
}
else low[x]=MIN(low[x],dfn[y]);
}
}

int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y; scanf("%d%d",&x,&y);
if(x==y)continue;
addedge(x,y); addedge(y,x);
}

for(int i=1;i<=n;i++)
if(!dfn[i])sroot=i,tarjan(i);

for(int i=1;i<=n;i++)
if(iscut[i])ans++;
printf("%d\n",ans);
for(int i=1;i<=n;i++)
if(iscut[i])printf("%d ",i);

return 0;
}

强连通分量缩点

题目描述:https://www.luogu.org/problemnew/show/P3387

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include<cstdio>
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
using namespace std;

const int maxn=1e4+3;
const int maxm=1e5+3;
struct Edge{int v,next;};
Edge edge[maxm],edge2[maxm];
int head[maxn],w[maxn],dfn[maxn],low[maxn],stac[maxn],color[maxn];
int n,m,tot,num,top,cnt,ans;
int head2[maxn],W[maxn],tot2,sum[maxn];
bool ins[maxn];

inline void addedge(int x,int y){
edge[++tot].v=y;
edge[tot].next=head[x];
head[x]=tot;
}

inline void addedge2(int x,int y){
edge2[++tot2].v=y;
edge2[tot2].next=head2[x];
head2[x]=tot2;
}

inline void input(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
}
}

int dfs(int x){
if(sum[x])return sum[x];
int temp=0;
for(int i=head2[x];i;i=edge2[i].next){
temp=MAX(temp,dfs(edge2[i].v));
}
sum[x]=W[x]+temp;
return sum[x];
}

void tarjan(int x){
low[x]=dfn[x]=++num;
stac[++top]=x; ins[x]=true;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].v;
if(!dfn[y]){
tarjan(y);
low[x]=MIN(low[x],low[y]);
}
else if(ins[y]){
low[x]=MIN(low[x],dfn[y]);
}
}
if(dfn[x]==low[x]){
++cnt; int y;
do{
y=stac[top--]; ins[y]=false;
color[y]=cnt;
W[cnt]+=w[y];
}while(x!=y);
}
}

int main(){
input();
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}

for(int x=1;x<=n;x++){
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].v;
if(color[x]!=color[y]){
addedge2(color[x],color[y]);
}
}
}
for(int i=1;i<=cnt;i++){
if(!sum[i]){
ans=MAX(ans,dfs(i));
}
}

printf("%d",ans);
return 0;
}

非压位高精

虽然重载的运算符两边数据类型都是Bigint,但因为自动强制转换,所以用[Bigint] * [int]也不会错。

在大数除int、大数对int取余时,效率不及专门功能的函数

参考:CSDN用户 代号4101

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#define MAX(a,b) ((a)>(b)?(a):(b))
using namespace std;

const int MAXLEN = 1000; //最长的数字长度

struct Bigint{
int d[MAXLEN],len;
void clean(){while(len>1&&!d[len-1])len--;} //去前导0

Bigint(){memset(d,0,sizeof(d));len=1;}
Bigint(int num){*this=num;}
Bigint(char* num){*this=num;}

Bigint operator = (const char* num){
len=strlen(num);
for(int i=0;i<len;i++)d[i]=num[len-1-i]-'0';
clean();
return *this;
}

Bigint operator = (int num){
char s[MAXLEN];
sprintf(s,"%d",num);
*this=s;
return *this;
}

Bigint operator + (const Bigint& b){ //只能大数加小数
Bigint c=*this; int i;
for(i=0;i<b.len;i++){
c.d[i]+=b.d[i];
if(c.d[i]>9)c.d[i]%=10,c.d[i+1]++;
}
while(c.d[i]>9)c.d[i++]%=10,c.d[i]++;
c.len=MAX(len,b.len);
if(c.d[i]&&c.len<=i)c.len=i+1;
return c;
}

Bigint operator - (const Bigint& b){ //不能用小数减大数
Bigint c=*this; int i;
for(i=0;i<b.len;i++){
c.d[i]-=b.d[i];
if(c.d[i]<0)c.d[i]+=10,c.d[i+1]--;
}
while(c.d[i]<0)c.d[i++]+=10,c.d[i]--;
c.clean();
return c;
}

Bigint operator * (const Bigint& b)const{
int i,j; Bigint c;
c.len=len+b.len;
for(j=0;j<b.len;j++)
for(i=0;i<len;i++)
c.d[i+j]+=d[i]*b.d[j];
for(i=0;i<c.len-1;i++)
c.d[i+1]+=c.d[i]/10,c.d[i]%=10;
c.clean();
return c;
}

Bigint operator / (const Bigint& b){
int i,j;
Bigint c=*this,a=0;
for(i=len-1;i>=0;i--){
a=a*10+d[i];
for(j=0;j<10;j++)if(a<b*(j+1))break;
c.d[i]=j;
a=a-b*j;
}
c.clean();
return c;
}

Bigint operator % (const Bigint& b){
int i,j; Bigint a=0;
for(i=len-1;i>=0;i--){
a=a*10+d[i];
for(j=0;j<10;j++)if(a<b*(j+1))break;
a=a-b*j;
}
return a;
}

Bigint operator += (const Bigint& b){
*this=*this+b;
return *this;
}

bool operator < (const Bigint& b)const{
if(len!=b.len)return len<b.len;
for(int i=len-1;i>=0;i--)
if(d[i]!=b.d[i])return d[i]<b.d[i];
return false;
}

bool operator > (const Bigint& b)const{return b<*this;}
bool operator <= (const Bigint& b)const{return !(b<*this);}
bool operator >= (const Bigint& b)const{return !(*this<b);}
bool operator != (const Bigint& b)const{return b<*this||*this<b;}
bool operator == (const Bigint& b)const{return !(b<*this)&&!(b>*this);}

string str()const{
char s[MAXLEN]={};
for(int i=0;i<len;i++)s[len-1-i]=d[i]+'0';
return s;
}
};

istream& operator >> (istream& in,Bigint& x){
string s; in>>s;
x=s.c_str();
return in;
}

ostream& operator << (ostream& out,const Bigint& x){
out<<x.str();
return out;
}

int main(){ //just an example...
Bigint s=0,t;
while(cin>>t){
if(t.len==1&&!t.d[0])break;
s=s+t;
}
cout<<s<<endl;
return 0;
}

压位高精

题面:https://www.luogu.com.cn/problem/P2152

自己缝的稍慢版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef unsigned int uint;
#define rint register int
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))

inline int rd() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return x*f;
}

const int N=10010;
const int base=10000;
const int limit=4;
int power_10[]={1,10,100,1000,1000,10000};

struct Bigint {
int a[N],len;
Bigint(){memset(a,0,sizeof(a)),len=0;}
void clean(){while(len>1&&!a[len-1])len--;} //去前导0
void init(LL x) {
while(x)a[len++]=x%base,x/=base;
}
void read() {
char s[N];
scanf("%s",s);
len=1;
int n=strlen(s),t=0;
for(rint i=n-1;~i;--i,++t) {
a[len-1]+=(s[i]-'0')*power_10[t];
if(t+1==limit)t=-1,++len;
}
if(!t)--len;
}
void print(char c=-1) {
printf("%lld",a[len-1]);
for(rint i=len-2;i>=0;--i)printf("%0*lld",limit,a[i]);
if(~c)putchar(c);
}

Bigint(int num){*this=num;}
Bigint(char* num){*this=num;}
Bigint operator = (const char* num){
len=strlen(num);
for(int i=0;i<len;i++)a[i]=num[len-1-i]-'0';
clean();
return *this;
}
Bigint operator = (int num){
char s[N];
sprintf(s,"%d",num);
*this=s;
return *this;
}

bool operator < (const Bigint& b)const{
if(len!=b.len)return len<b.len;
for(rint i=len-1;~i;i--)
if(a[i]!=b.a[i])return a[i]<b.a[i];
return false;
}
bool operator > (const Bigint& b)const{return b<*this;}
bool operator <= (const Bigint& b)const{return !(b<*this);}
bool operator >= (const Bigint& b)const{return !(*this<b);}
bool operator != (const Bigint& b)const{return b<*this||*this<b;}
bool operator == (const Bigint& b)const{return !(b<*this)&&!(b>*this);}
};
Bigint operator + (const Bigint &a,const Bigint &b) {
Bigint c;int mx=MAX(a.len,b.len);c.len=mx;
for(rint i=0;i<mx;++i)c.a[i]=a.a[i]+b.a[i];
for(rint i=0;i<mx;++i)if(c.a[i]>=base)++c.a[i+1],c.a[i]-=base;
if(c.a[c.len])++c.len;
return c;
}
Bigint operator - (const Bigint &a,const Bigint &b) {
Bigint c;int mx=a.len;c.len=mx;
for(rint i=0;i<mx;++i)c.a[i]=a.a[i]-b.a[i];
for(rint i=0;i<mx;++i)if(c.a[i]<0)--c.a[i+1],c.a[i]+=base;
while(c.len&&!c.a[c.len-1])--c.len;
return c;
}
Bigint operator * (const Bigint &a,const Bigint &b) {
Bigint c;int mx=a.len+b.len-1;c.len=mx;
for(rint i=0,mxa=a.len;i<mxa;++i)
for(rint j=0,mxb=b.len;j<mxb;++j) {
c.a[i+j]+=a.a[i]*b.a[j];
if(c.a[i+j]>=base)c.a[i+j+1]+=c.a[i+j]/base,c.a[i+j]%=base;
}
if(c.a[c.len])++c.len;
return c;
}
Bigint operator / (const Bigint &a,const int &b) {
Bigint c;int mx=a.len;
LL now=0;
for(rint i=mx-1,s=0;~i;--i) {
now=now*base+a.a[i];
if(now/b)s=1;
if(!s)continue;
c.a[c.len++]=now/b;
now%=b;
}
reverse(c.a,c.a+c.len);
return c;
}

Bigint biggcd(Bigint x,Bigint y){
int xcnt2=0, ycnt2=0;
while(!(x.a[0]&1)){xcnt2++; x=x/2;}
while(!(y.a[0]&1)){ycnt2++; y=y/2;}
int z=MIN(xcnt2,ycnt2);
while(x!=y){
if(x<y){swap(x,y);}
x=x-y;
while(!(x.a[0]&1)){x=x/2;}
}
Bigint temp = 2;
while(z--){x=x*temp;}
return x;
}

int main(){
Bigint a,b;
a.read(); b.read();
biggcd(a,b).print();
return 0;
}

下面给出更好的版本,注意与原代码有改动:

  • 增加了biggcd
  • 在 class 中图省事将所有权限改成了 public ,这并不推荐
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
//来源: https://paste.ubuntu.com/p/7VKYzpC7dn/
//作者:小黑AWM+MashPlant
//注:可以直接把BigInt和一样用cin cout都行,就是高精乘为了速度才用了FFT降低了精度,有需要可以自行更改。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
using namespace std;
const double PI = acos(-1.0);
struct Complex{
double x,y;
Complex(double _x = 0.0,double _y = 0.0){
x = _x;
y = _y;
}
Complex operator-(const Complex &b)const{
return Complex(x - b.x,y - b.y);
}
Complex operator+(const Complex &b)const{
return Complex(x + b.x,y + b.y);
}
Complex operator*(const Complex &b)const{
return Complex(x*b.x - y*b.y,x*b.y + y*b.x);
}
};
void change(Complex y[],int len){
int i,j,k;
for(int i = 1,j = len/2;i<len-1;i++){
if(i < j) swap(y[i],y[j]);
k = len/2;
while(j >= k){
j = j - k;
k = k/2;
}
if(j < k) j+=k;
}
}
void fft(Complex y[],int len,int on){
change(y,len);
for(int h = 2;h <= len;h<<=1){
Complex wn(cos(on*2*PI/h),sin(on*2*PI/h));
for(int j = 0;j < len;j += h){
Complex w(1,0);
for(int k = j;k < j + h/2;k++){
Complex u = y[k];
Complex t = w*y[k + h/2];
y[k] = u + t;
y[k + h/2] = u - t;
w = w*wn;
}
}
}
if(on == -1){
for(int i = 0;i < len;i++){
y[i].x /= len;
}
}
}
class BigInt
{
public:
#define Value(x, nega) ((nega) ? -(x) : (x))
#define At(vec, index) ((index) < vec.size() ? vec[(index)] : 0)
static int absComp(const BigInt &lhs, const BigInt &rhs)
{
if (lhs.size() != rhs.size())
return lhs.size() < rhs.size() ? -1 : 1;
for (int i = lhs.size() - 1; i >= 0; --i)
if (lhs[i] != rhs[i])
return lhs[i] < rhs[i] ? -1 : 1;
return 0;
}
using Long = long long;
const static int Exp = 9;
const static Long Mod = 1000000000;
mutable std::vector<Long> val;
mutable bool nega = false;
void trim() const
{
while (val.size() && val.back() == 0)
val.pop_back();
if (val.empty())
nega = false;
}
int size() const { return val.size(); }
Long &operator[](int index) const { return val[index]; }
Long &back() const { return val.back(); }
BigInt(int size, bool nega) : val(size), nega(nega) {}
BigInt(const std::vector<Long> &val, bool nega) : val(val), nega(nega) {}

friend std::ostream &operator<<(std::ostream &os, const BigInt &n)
{
if (n.size())
{
if (n.nega)
putchar('-');
for (int i = n.size() - 1; i >= 0; --i)
{
if (i == n.size() - 1)
printf("%lld", n[i]);
else
printf("%0*lld", n.Exp, n[i]);
}
}
else
putchar('0');
return os;
}
friend BigInt operator+(const BigInt &lhs, const BigInt &rhs)
{
BigInt ret(lhs);
return ret += rhs;
}
friend BigInt operator-(const BigInt &lhs, const BigInt &rhs)
{
BigInt ret(lhs);
return ret -= rhs;
}
BigInt(Long x = 0)
{
if (x < 0)
x = -x, nega = true;
while (x >= Mod)
val.push_back(x % Mod), x /= Mod;
if (x)
val.push_back(x);
}
BigInt(const char *s)
{
int bound = 0, pos;
if (s[0] == '-')
nega = true, bound = 1;
Long cur = 0, pow = 1;
for (pos = strlen(s) - 1; pos >= Exp + bound - 1; pos -= Exp, val.push_back(cur), cur = 0, pow = 1)
for (int i = pos; i > pos - Exp; --i)
cur += (s[i] - '0') * pow, pow *= 10;
for (cur = 0, pow = 1; pos >= bound; --pos)
cur += (s[pos] - '0') * pow, pow *= 10;
if (cur)
val.push_back(cur);
}
BigInt &operator=(const char *s){
BigInt n(s);
*this = n;
return n;
}
BigInt &operator=(const Long x){
BigInt n(x);
*this = n;
return n;
}
friend std::istream &operator>>(std::istream &is, BigInt &n){
string s;
is >> s;
n=(char*)s.data();
return is;
}
BigInt &operator+=(const BigInt &rhs)
{
const int cap = std::max(size(), rhs.size()) + 1;
val.resize(cap);
int carry = 0;
for (int i = 0; i < cap - 1; ++i)
{
val[i] = Value(val[i], nega) + Value(At(rhs, i), rhs.nega) + carry, carry = 0;
if (val[i] >= Mod)
val[i] -= Mod, carry = 1;
else if (val[i] < 0)
val[i] += Mod, carry = -1;
}
if ((val.back() = carry) == -1) //assert(val.back() == 1 or 0 or -1)
{
nega = true, val.pop_back();
bool tailZero = true;
for (int i = 0; i < cap - 1; ++i)
{
if (tailZero && val[i])
val[i] = Mod - val[i], tailZero = false;
else
val[i] = Mod - 1 - val[i];
}
}
trim();
return *this;
}
friend BigInt operator-(const BigInt &rhs)
{
BigInt ret(rhs);
ret.nega ^= 1;
return ret;
}
BigInt &operator-=(const BigInt &rhs)
{
rhs.nega ^= 1;
*this += rhs;
rhs.nega ^= 1;
return *this;
}
friend BigInt operator*(const BigInt &lhs, const BigInt &rhs)
{
int len=1;
BigInt ll=lhs,rr=rhs;
ll.nega = lhs.nega ^ rhs.nega;
while(len<2*lhs.size()||len<2*rhs.size())len<<=1;
ll.val.resize(len),rr.val.resize(len);
Complex x1[len],x2[len];
for(int i=0;i<len;i++){
Complex nx(ll[i],0.0),ny(rr[i],0.0);
x1[i]=nx;
x2[i]=ny;
}
fft(x1,len,1);
fft(x2,len,1);
for(int i = 0 ; i < len; i++)
x1[i] = x1[i] * x2[i];
fft( x1 , len , -1 );
for(int i = 0 ; i < len; i++)
ll[i] = int( x1[i].x + 0.5 );
for(int i = 0 ; i < len; i++){
ll[i+1]+=ll[i]/Mod;
ll[i]%=Mod;
}
ll.trim();
return ll;
}
friend BigInt operator*(const BigInt &lhs, const Long &x){
BigInt ret=lhs;
bool negat = ( x < 0 );
Long xx = (negat) ? -x : x;
ret.nega ^= negat;
ret.val.push_back(0);
ret.val.push_back(0);
for(int i = 0; i < ret.size(); i++)
ret[i]*=xx;
for(int i = 0; i < ret.size(); i++){
ret[i+1]+=ret[i]/Mod;
ret[i] %= Mod;
}
ret.trim();
return ret;
}
BigInt &operator*=(const BigInt &rhs) { return *this = *this * rhs; }
BigInt &operator*=(const Long &x) { return *this = *this * x; }
friend BigInt operator/(const BigInt &lhs, const BigInt &rhs)
{
static std::vector<BigInt> powTwo{BigInt(1)};
static std::vector<BigInt> estimate;
estimate.clear();
if (absComp(lhs, rhs) < 0)
return BigInt();
BigInt cur = rhs;
int cmp;
while ((cmp = absComp(cur, lhs)) <= 0)
{
estimate.push_back(cur), cur += cur;
if (estimate.size() >= powTwo.size())
powTwo.push_back(powTwo.back() + powTwo.back());
}
if (cmp == 0)
return BigInt(powTwo.back().val, lhs.nega ^ rhs.nega);
BigInt ret = powTwo[estimate.size() - 1];
cur = estimate[estimate.size() - 1];
for (int i = estimate.size() - 1; i >= 0 && cmp != 0; --i)
if ((cmp = absComp(cur + estimate[i], lhs)) <= 0)
cur += estimate[i], ret += powTwo[i];
ret.nega = lhs.nega ^ rhs.nega;
return ret;
}
friend BigInt operator/(const BigInt &num,const Long &x){
bool negat = ( x < 0 );
Long xx = (negat) ? -x : x;
BigInt ret;
Long k = 0;
ret.val.resize( num.size() );
ret.nega = (num.nega ^ negat);
for(int i = num.size() - 1 ;i >= 0; i--){
ret[i] = ( k * Mod + num[i]) / xx;
k = ( k * Mod + num[i]) % xx;
}
ret.trim();
return ret;
}
bool operator==(const BigInt &rhs) const
{
return nega == rhs.nega && val == rhs.val;
}
bool operator!=(const BigInt &rhs) const { return nega != rhs.nega || val != rhs.val; }
bool operator>=(const BigInt &rhs) const { return !(*this < rhs); }
bool operator>(const BigInt &rhs) const { return !(*this <= rhs); }
bool operator<=(const BigInt &rhs) const
{
if (nega && !rhs.nega)
return true;
if (!nega && rhs.nega)
return false;
int cmp = absComp(*this, rhs);
return nega ? cmp >= 0 : cmp <= 0;
}
bool operator<(const BigInt &rhs) const
{
if (nega && !rhs.nega)
return true;
if (!nega && rhs.nega)
return false;
return (absComp(*this, rhs) < 0) ^ nega;
}
void swap(const BigInt &rhs) const
{
std::swap(val, rhs.val);
std::swap(nega, rhs.nega);
}
};

BigInt biggcd(BigInt x,BigInt y){
int xcnt2=0, ycnt2=0;
while(!(x.val[0]&1)){xcnt2++; x=x/2;}
while(!(y.val[0]&1)){ycnt2++; y=y/2;}
int z=MIN(xcnt2,ycnt2);
while(x!=y){
if(x<y){swap(x,y);}
x=x-y;
while(!(x.val[0]&1)){x=x/2;}
}
BigInt temp = 2;
while(z--){x=x*temp;}
return x;
}
/*
BigInt ba,bb;
int main(){
cin>>ba>>bb;
cout << ba + bb << '\n';//和
cout << ba - bb << '\n';//差
cout << ba * bb << '\n';//积
BigInt d;
cout << (d = ba / bb) << '\n';//商
cout << ba - d * bb << '\n';//余
return 0;
}*/

BigInt a,b;
int main(){
cin>>a>>b;
cout<<biggcd(a,b);
return 0;
}

作为对比,给出原作者的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
//作者:小黑AWM+MashPlant
//注:可以直接把BigInt和一样用cin cout都行,就是高精乘为了速度才用了FFT降低了精度,有需要可以自行更改。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const double PI = acos(-1.0);
struct Complex{
double x,y;
Complex(double _x = 0.0,double _y = 0.0){
x = _x;
y = _y;
}
Complex operator-(const Complex &b)const{
return Complex(x - b.x,y - b.y);
}
Complex operator+(const Complex &b)const{
return Complex(x + b.x,y + b.y);
}
Complex operator*(const Complex &b)const{
return Complex(x*b.x - y*b.y,x*b.y + y*b.x);
}
};
void change(Complex y[],int len){
int i,j,k;
for(int i = 1,j = len/2;i<len-1;i++){
if(i < j) swap(y[i],y[j]);
k = len/2;
while(j >= k){
j = j - k;
k = k/2;
}
if(j < k) j+=k;
}
}
void fft(Complex y[],int len,int on){
change(y,len);
for(int h = 2;h <= len;h<<=1){
Complex wn(cos(on*2*PI/h),sin(on*2*PI/h));
for(int j = 0;j < len;j += h){
Complex w(1,0);
for(int k = j;k < j + h/2;k++){
Complex u = y[k];
Complex t = w*y[k + h/2];
y[k] = u + t;
y[k + h/2] = u - t;
w = w*wn;
}
}
}
if(on == -1){
for(int i = 0;i < len;i++){
y[i].x /= len;
}
}
}
class BigInt
{
#define Value(x, nega) ((nega) ? -(x) : (x))
#define At(vec, index) ((index) < vec.size() ? vec[(index)] : 0)
static int absComp(const BigInt &lhs, const BigInt &rhs)
{
if (lhs.size() != rhs.size())
return lhs.size() < rhs.size() ? -1 : 1;
for (int i = lhs.size() - 1; i >= 0; --i)
if (lhs[i] != rhs[i])
return lhs[i] < rhs[i] ? -1 : 1;
return 0;
}
using Long = long long;
const static int Exp = 9;
const static Long Mod = 1000000000;
mutable std::vector<Long> val;
mutable bool nega = false;
void trim() const
{
while (val.size() && val.back() == 0)
val.pop_back();
if (val.empty())
nega = false;
}
int size() const { return val.size(); }
Long &operator[](int index) const { return val[index]; }
Long &back() const { return val.back(); }
BigInt(int size, bool nega) : val(size), nega(nega) {}
BigInt(const std::vector<Long> &val, bool nega) : val(val), nega(nega) {}

public:
friend std::ostream &operator<<(std::ostream &os, const BigInt &n)
{
if (n.size())
{
if (n.nega)
putchar('-');
for (int i = n.size() - 1; i >= 0; --i)
{
if (i == n.size() - 1)
printf("%lld", n[i]);
else
printf("%0*lld", n.Exp, n[i]);
}
}
else
putchar('0');
return os;
}
friend BigInt operator+(const BigInt &lhs, const BigInt &rhs)
{
BigInt ret(lhs);
return ret += rhs;
}
friend BigInt operator-(const BigInt &lhs, const BigInt &rhs)
{
BigInt ret(lhs);
return ret -= rhs;
}
BigInt(Long x = 0)
{
if (x < 0)
x = -x, nega = true;
while (x >= Mod)
val.push_back(x % Mod), x /= Mod;
if (x)
val.push_back(x);
}
BigInt(const char *s)
{
int bound = 0, pos;
if (s[0] == '-')
nega = true, bound = 1;
Long cur = 0, pow = 1;
for (pos = strlen(s) - 1; pos >= Exp + bound - 1; pos -= Exp, val.push_back(cur), cur = 0, pow = 1)
for (int i = pos; i > pos - Exp; --i)
cur += (s[i] - '0') * pow, pow *= 10;
for (cur = 0, pow = 1; pos >= bound; --pos)
cur += (s[pos] - '0') * pow, pow *= 10;
if (cur)
val.push_back(cur);
}
BigInt &operator=(const char *s){
BigInt n(s);
*this = n;
return n;
}
BigInt &operator=(const Long x){
BigInt n(x);
*this = n;
return n;
}
friend std::istream &operator>>(std::istream &is, BigInt &n){
string s;
is >> s;
n=(char*)s.data();
return is;
}
BigInt &operator+=(const BigInt &rhs)
{
const int cap = std::max(size(), rhs.size()) + 1;
val.resize(cap);
int carry = 0;
for (int i = 0; i < cap - 1; ++i)
{
val[i] = Value(val[i], nega) + Value(At(rhs, i), rhs.nega) + carry, carry = 0;
if (val[i] >= Mod)
val[i] -= Mod, carry = 1;
else if (val[i] < 0)
val[i] += Mod, carry = -1;
}
if ((val.back() = carry) == -1) //assert(val.back() == 1 or 0 or -1)
{
nega = true, val.pop_back();
bool tailZero = true;
for (int i = 0; i < cap - 1; ++i)
{
if (tailZero && val[i])
val[i] = Mod - val[i], tailZero = false;
else
val[i] = Mod - 1 - val[i];
}
}
trim();
return *this;
}
friend BigInt operator-(const BigInt &rhs)
{
BigInt ret(rhs);
ret.nega ^= 1;
return ret;
}
BigInt &operator-=(const BigInt &rhs)
{
rhs.nega ^= 1;
*this += rhs;
rhs.nega ^= 1;
return *this;
}
friend BigInt operator*(const BigInt &lhs, const BigInt &rhs)
{
int len=1;
BigInt ll=lhs,rr=rhs;
ll.nega = lhs.nega ^ rhs.nega;
while(len<2*lhs.size()||len<2*rhs.size())len<<=1;
ll.val.resize(len),rr.val.resize(len);
Complex x1[len],x2[len];
for(int i=0;i<len;i++){
Complex nx(ll[i],0.0),ny(rr[i],0.0);
x1[i]=nx;
x2[i]=ny;
}
fft(x1,len,1);
fft(x2,len,1);
for(int i = 0 ; i < len; i++)
x1[i] = x1[i] * x2[i];
fft( x1 , len , -1 );
for(int i = 0 ; i < len; i++)
ll[i] = int( x1[i].x + 0.5 );
for(int i = 0 ; i < len; i++){
ll[i+1]+=ll[i]/Mod;
ll[i]%=Mod;
}
ll.trim();
return ll;
}
friend BigInt operator*(const BigInt &lhs, const Long &x){
BigInt ret=lhs;
bool negat = ( x < 0 );
Long xx = (negat) ? -x : x;
ret.nega ^= negat;
ret.val.push_back(0);
ret.val.push_back(0);
for(int i = 0; i < ret.size(); i++)
ret[i]*=xx;
for(int i = 0; i < ret.size(); i++){
ret[i+1]+=ret[i]/Mod;
ret[i] %= Mod;
}
ret.trim();
return ret;
}
BigInt &operator*=(const BigInt &rhs) { return *this = *this * rhs; }
BigInt &operator*=(const Long &x) { return *this = *this * x; }
friend BigInt operator/(const BigInt &lhs, const BigInt &rhs)
{
static std::vector<BigInt> powTwo{BigInt(1)};
static std::vector<BigInt> estimate;
estimate.clear();
if (absComp(lhs, rhs) < 0)
return BigInt();
BigInt cur = rhs;
int cmp;
while ((cmp = absComp(cur, lhs)) <= 0)
{
estimate.push_back(cur), cur += cur;
if (estimate.size() >= powTwo.size())
powTwo.push_back(powTwo.back() + powTwo.back());
}
if (cmp == 0)
return BigInt(powTwo.back().val, lhs.nega ^ rhs.nega);
BigInt ret = powTwo[estimate.size() - 1];
cur = estimate[estimate.size() - 1];
for (int i = estimate.size() - 1; i >= 0 && cmp != 0; --i)
if ((cmp = absComp(cur + estimate[i], lhs)) <= 0)
cur += estimate[i], ret += powTwo[i];
ret.nega = lhs.nega ^ rhs.nega;
return ret;
}
friend BigInt operator/(const BigInt &num,const Long &x){
bool negat = ( x < 0 );
Long xx = (negat) ? -x : x;
BigInt ret;
Long k = 0;
ret.val.resize( num.size() );
ret.nega = (num.nega ^ negat);
for(int i = num.size() - 1 ;i >= 0; i--){
ret[i] = ( k * Mod + num[i]) / xx;
k = ( k * Mod + num[i]) % xx;
}
ret.trim();
return ret;
}
bool operator==(const BigInt &rhs) const
{
return nega == rhs.nega && val == rhs.val;
}
bool operator!=(const BigInt &rhs) const { return nega != rhs.nega || val != rhs.val; }
bool operator>=(const BigInt &rhs) const { return !(*this < rhs); }
bool operator>(const BigInt &rhs) const { return !(*this <= rhs); }
bool operator<=(const BigInt &rhs) const
{
if (nega && !rhs.nega)
return true;
if (!nega && rhs.nega)
return false;
int cmp = absComp(*this, rhs);
return nega ? cmp >= 0 : cmp <= 0;
}
bool operator<(const BigInt &rhs) const
{
if (nega && !rhs.nega)
return true;
if (!nega && rhs.nega)
return false;
return (absComp(*this, rhs) < 0) ^ nega;
}
void swap(const BigInt &rhs) const
{
std::swap(val, rhs.val);
std::swap(nega, rhs.nega);
}
};
BigInt ba,bb;
int main(){
cin>>ba>>bb;
cout << ba + bb << '\n';//和
cout << ba - bb << '\n';//差
cout << ba * bb << '\n';//积
BigInt d;
cout << (d = ba / bb) << '\n';//商
cout << ba - d * bb << '\n';//余
return 0;
}

二分图最大匹配

题目链接:https://www.luogu.com.cn/problem/P3386

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<cstdio>
#include<cstring>
using namespace std;

const int maxvertex=503<<1;
const int maxedge=5e4+3;
int tot,n,m,e,ans;
int head[maxvertex],match[maxvertex>>1];
struct Edge{int v,next;}edge[maxedge];
bool vis[maxvertex>>1];

void addedge(int x,int y){
edge[++tot].v=y;
edge[tot].next=head[x];
head[x]=tot;
}

bool dfs(int x){
for(int i=head[x],y;i;i=edge[i].next){
if(!vis[y=edge[i].v]){
vis[y]=true;
if((!match[y]) || dfs(match[y])){
match[y]=x;
return true;
}
}
}
return false;
}

int main(){
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=e;i++){
int x,y; scanf("%d%d",&x,&y);
addedge(x,y);
}

for(int i=1;i<=n;i++){
memset(vis,false,sizeof(vis));
if(dfs(i)){ans++;}
}
printf("%d",ans);
return 0;
}

字典树 (Trie)

本文章的 Trie 内容参考自 oi-wiki.org/string/trie

结构体封装的模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct trie {
int nex[100000][26], cnt;
bool exist[100000]; // 该结点结尾的字符串是否存在

void insert(char *s, int l) { // 插入字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) nex[p][c] = ++cnt; // 如果没有,就添加结点
p = nex[p][c];
}
exist[p] = 1;
}

bool find(char *s, int l) { // 查找字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) return 0;
p = nex[p][c];
}
return exist[p];
}
};

题目: https://www.luogu.com.cn/problem/P2580

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <cstdio>

const int MAXN = 5e5+3;
char s[55];
int n,m,nex[MAXN][26],tag[MAXN],cnt=1;

int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s+1);
int p = 1;
for(int j=1;s[j];j++){
int c = s[j]-'a';
if(!nex[p][c])nex[p][c]=++cnt;
p=nex[p][c];
}
tag[p] = 1;
}
scanf("%d",&m);
while(m--){
scanf("%s",s+1);
int p = 1;
for(int j=1;s[j];j++){
int c = s[j]-'a';
p=nex[p][c];
if(!p)break;
}
if(tag[p]==1){
tag[p]=2;
puts("OK");
}
else if(tag[p]==2) puts("REPEAT");
else puts("WRONG");
}
return 0;
}

01-Trie

题面: https://www.luogu.com.cn/problem/P4551

注意:以下按数位权值从高到低建立trie

9-5.jpg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <cstdio>
#define MAX(a,b) ((a)>(b)?(a):(b))
using namespace std;

const int maxn = 1e5+3;
const int maxm = maxn-1;
struct Edge{int v,w,next;}edge[maxm<<1];
int head[maxn],tot,dis[maxn];
int n,m,ans,cnt=1,nex[maxn<<5][2];

inline void addedge(int x,int y,int z){
edge[++tot].v=y;
edge[tot].w=z;
edge[tot].next=head[x];
head[x]=tot;
}

void insert(int x){
for(int i=30,p=1;~i;i--){
int c = ((x>>i)&1); //二进制从(权值)高位向低位取
if(!nex[p][c])nex[p][c]=++cnt;
p=nex[p][c];
}
}

void get(int x){
int res = 0;
for(int i=30,p=1;~i;i--){
int c = ((x>>i)&1);
if(nex[p][c^1]){ //贪心
p=nex[p][c^1];
res |= (1<<i); //第i位(从低权位向高权位数)置1
}
else p=nex[p][c];
}
ans=MAX(ans,res);
}

void dfs(int x, int fa){
insert(dis[x]);
get(dis[x]);
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].v;
if(y==fa)continue;
dis[y]=dis[x]^edge[i].w;
dfs(y,x);
}
}

int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
addedge(y,x,z);
}

dfs(1,0);

printf("%d",ans);
return 0;
}

维护异或和

注意:以下按权值从低位到高位建立trie

对于每一个节点,记录以下三个量:

  • ch[o][0/1] 指节点 o 的两个儿子,ch[o][0]指下一位是 0,同理ch[o][1]指下一位是 1。
  • w[o]指节点 o 到其父亲节点这条边上数值的数量(权值)。每插入一个数字xx二进制拆分后在 trie 上路径的权值都会+1
  • xorv[o]指以 o 为根的子树维护的异或和。
1
2
3
4
5
6
7
8
9
10
11
12
void maintain(int o){
w[o] = xorv[o] = 0;
if(ch[o][0]){
w[o] += w[ch[o][0]];
xorv[o] ^= xorv[ch[o][0]] << 1; //挪位置,末位补0,对应o到ch[o][0]这条边
}
if(ch[o][1]){
w[o] += w[ch[o][1]];
xorv[o] ^= (xorv[ch[o][1]] << 1) | (w[ch[o][1]] & 1);
} //因为ch[o][1]是经过1从o下来的,所以这个“经过的1”的这一位的异或和就是w[ch[o][1]]的奇偶性
w[o] = w[o] & 1; //这句话删掉也可以?因为上文就只利用了他的奇偶性。
}

插入和删除,只需要修改叶子节点的w[]即可,在回溯的过程中一路维护:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace trie {
const int MAXH = 21;
int ch[_ * (MAXH + 1)][2], w[_ * (MAXH + 1)], xorv[_ * (MAXH + 1)];
int tot = 0; // 这里的 _ 是一个 int 型

int mknode() {
++tot;
ch[tot][1] = ch[tot][0] = w[tot] = xorv[tot] = 0;
return tot;
}

void maintain(int o) {
w[o] = xorv[o] = 0;
if (ch[o][0]) {
w[o] += w[ch[o][0]];
xorv[o] ^= xorv[ch[o][0]] << 1;
}
if (ch[o][1]) {
w[o] += w[ch[o][1]];
xorv[o] ^= (xorv[ch[o][1]] << 1) | (w[ch[o][1]] & 1);
}
w[o] = w[o] & 1;
}

void insert(int &o, int x, int dp) { //看懂过,没时间别再看
if (!o) o = mknode();
if (dp > MAXH) return (void)(w[o]++);
insert(ch[o][x & 1], x >> 1, dp + 1);
maintain(o);
}

void erase(int o, int x, int dp) {
if (dp > 20) return (void)(w[o]--);
erase(ch[o][x & 1], x >> 1, dp + 1);
maintain(o);
}
} // namespace trie

注意:这里的MAXH指 trie 的深度,也就是强制让每一个叶子节点到根的距离为MAXH。对于一些比较小的值,可能有时候不需要建立这么深(例如:如果插入数字4,分解成二进制后为100,从根开始插入001这三位即可),但是我们强制插入MAXH位。这样做的目的是为了便于全局+1时处理进位。例如:如果原数字是311),递增之后变成4100),如果当初插入3时只插入了2位,那这里的进位就没了。