HN CSP-J/S 2022 RP++ !

序号 算法
SPFA
并查集
最小生成树
拓扑排序
字典树
N 懒得加了


1. SPFA

题目链接

题目描述

输入的第一行是四个整数,代表站点个数 n 和线路条数 m ,出发点 x 和 目的地 y 。

第 2 到第 (m + 1) 行,每行三个整数 u, v, w 代表存在一条从 u 出发到达 v 的线路,边权为 w。

输出从 x 到 y 点的最短路径。

code

#include <bits/stdc++.h>
using namespace std ;

int n , m , x , y ;
int tot ;
int head[1000005] ;
int dis[1000005] ;
int b[1000005] ;

struct node {
	int u , v , w , next ;
} e[1000005] ; 

void add ( int u , int v, int w ) {
	tot++ ;
	e[tot].u = u ;
	e[tot].v = v ;
	e[tot].w = w ;
	e[tot].next = head[u] ;
	head[u] = tot ;
}

void spfa ( int s ) {
	queue < int > q ;
	for ( int i = 1 ; i <= n ; i++ ) {
		dis[i] = 1E15 ;
	}
	b[s] = 1 ;
	dis[s] = 0 ;
	q.push ( s ) ;
	while ( ! q.empty( ) ) {
		int u = q.front( ) ;
		q.pop( ) ;
		b[u] = 0 ;
		for ( int i = 1 ; i != 0 ; i = e[i].next ) {
			int v = e[i].v ;
			int w = e[i].w ;
			if ( dis[u] + w < dis[v] ) {
				dis[v] = dis[u] + w ;
				if ( b[v] == 0 ) {
					b[v] = 1 ;
					q.push ( v ) ;
				}
			}
		}
	}
}

int main ( ) {
	
	cin >> n >> m >> x >> y ;
	for ( int i = 1 ; i <= m ; i++ ) {
		int u , v , w ;
		cin >> u >> v >> w ;
		add ( u , v , w ) ;
	}
	spfa( x ) ;
	
	cout << dis[y] ;
	
	return 0 ;
}

2. 并查集

题目链接

PS:注意要将 fa 数组指向自己  

code

#include <bits/stdc++.h>
using namespace std ;

int n , m ;
int fa[10005] ;

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

int main ( ) {
	
	cin >> n >> m ;
	for ( int i = 1 ; i <= n ; i++ ) {
		fa[i] = i ;
	}
	for ( int i = 1 ; i <= m ; i++ ) {
		int z , x , y ;
		cin >> z >> x >> y ;
		x = find ( x ) ;
		y = find ( y ) ;
		if ( z == 1 ) {
			fa[x] = y;
		}
		else if ( z == 2 ) {
			if ( x == y ) {
				cout << "Y" << endl ;
			}
			else {
				cout << "N" << endl ;
			}
		}
	} 
	
	return 0 ;
} 

3. 最小生成树

题目链接

code

#include <bits/stdc++.h>
using namespace std ;

int n , m ;
int tot , cnt , sum ;
int fa[500005] ;

struct node {
	int u , v , w , next ;
} e[400005] ;  

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

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

int main ( ) {
	
	cin >> n >> m ;
	for ( int i = 1 ; i <= n ; i++ ) {
		fa[i] = i ;
	}
	for ( int i = 1 ; i <= m ; i++ ) {
		cin >> e[i].u >> e[i].v >> e[i].w ;
	}
	
	sort ( e + 1 , e + m + 1 , cmp ) ;
	
	for ( int i = 1 ; i <= m ; i++ ) {
		int u = e[i].u ;
		int v = e[i].v ;
		int w = e[i].w ;
		u = find ( u ) ;
		v = find ( v ) ;
		if ( u != v ) {
			fa[u] = v ;
			cnt++ ;
			sum += w ;
		}
		if ( cnt == n - 1 ) {
			cout << sum ;
			return 0 ;
		}
	}
	
	cout << "orz" ;
	
	return 0 ;
}

4. 拓扑排序

题目链接

code

#include <bits/stdc++.h>
using namespace std ;

int n , m ;
int ans ;
int head[5005] ;
int ind[5005] ;
int sum[5005] ;

struct node {
	int u , v , next ;
} e[500005] ;

void topsort ( ) {
	queue<int> q ;
	for ( int i = 1 ; i <= n ; i++ ) {
		if ( ind[i] == 0 ) {
			q.push( i ) ;
			sum[i] = 1 ;
		}
	}
	while ( !q.empty() ) {
		int u = q.front() ;
		q.pop();
		for ( int i = head[u] ; i != 0 ; i = e[i].next ) {
			int v = e[i].v ;
			ind[v]--;
			sum[v] += sum[u] ;  // 继承上一个结点 
			sum[v] %= 80112002 ;
			if ( ind[v] == 0 ) {
				q.push( v ) ;
			}
		}
	}
}

int main() {
	
	cin >> n >> m ;
	for ( int i = 1 ; i <= m ; i++ ) {
		int u , v ;
		cin >> u >> v ;
		e[i].u = u ;
		e[i].v = v ;
		e[i].next = head[u] ;
		head[u] = i ;
		ind[v]++ ;  // 出度 +1  
	}
	topsort();
	for ( int i = 1 ; i <= n ; i++ ) {
		if ( head[i] == 0 ) {
			ans += sum[i] ;
			ans %= 80112002 ;  // 提前模 防止爆数组 
		}
	}
	
	cout << ans ;
	
	return 0 ;
}


5. 堆

题目模板

code

#include <bits/stdc++.h>
using namespace std ;

priority_queue < int , vector < int > , greater < int > > q ;

int main ( ) {
	
	int n ;
	cin >> n ;
	for ( int i = 1 ; i <= n ; i++ ) {
		int op ;
		cin >> op ;
		if ( op == 1 ) {
			int x ;
			cin >> x ;
			q.push ( x ) ;
		}
		if ( op == 2 ) {
			cout << q.top ( ) << endl ;
		}
		if ( op == 3 ) {
			q.pop ( ) ;
		}
	}
	
	return 0 ;
}

6. 字典树

题目链接

code

#include<bits/stdc++.h>
using namespace std;

int t;
int cnt;
int tri[4000005][65];
long long ans[4000005];

int bh( char a ){
	if( a >= 'A' && a <= 'Z' ) {
		return a - 'A';
	}
	else if( a >= 'a' && a <= 'z' ) {
		return a - 'a' + 26;
	}
	else if( a >= '0' && a <= '9' ) {
		return a - '0' + 52; 
	}
} 

void build( string a ){
	int len = a.size();
	int root = 0;
	for( int i = 0 ; i < len ; i++ ) {
		int x = bh( a[i] );
		if( !tri[root][x] ) {
			++cnt , tri[root][x] = cnt;
			root = cnt;
		}
		else {
			root = tri[root][x];
		}
		ans[root]++;
	}
}

int query( string a ){
	int len = a.size();
	int root = 0;
	for( int i = 0 ; i < len ; i++ )
	{
		int x = bh( a[i] );
		if( !tri[root][x] ) return 0;
		else root = tri[root][x];
	}
	return ans[root];
}

void qk(){
	for( int i = 0 ; i <= cnt ; i++ ) {
		for( int j = 0 ; j <= 64 ; j++ ) {
			tri[i][j] = 0;
		}
	}
	for( int i = 1 ; i <= cnt ; i++ ) {
		ans[i] = 0;
	}
	cnt = 0;
}

void work(){
	
	qk();
	int n , q;
	cin >> n >> q;
	for( int i = 1 ; i <= n ; i++ ) {
		string a;
		cin >> a;
		build( a );
	}
	for( int i = 1 ; i <= q ; i++ ) {
		string a;
		cin >> a;
		cout << query( a ) << endl;
	}
}

int main(){
	
	cin >> t;
	for( int i = 1 ; i <= t ; i++ ) {
		work();
	}
	
	return 0;
}

二分查找

元素最后出现位置 等于upper_bound

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,m,i,x,ans;
int a[1000005]; 
int main()
{
 scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
 for(i=1;i<=m;i++)
   {
    scanf("%d",&x);
    int li=1,ri=n,mi;
    while(li<=ri)
      {
	   mi=li+(ri-li)/2;
	   if(x<a[mid]) ri=mi-1;
	          else {ans=mi;li=mi+1;}
	  } 
	 if(a[ans]==x) printf("%d ",ans);
	        else   printf("-1 ");
    }
 return 0;
}


 元素最先出现位置 等于lower_bound
 
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,m,i,x,ans;
int a[1000010]; 
int main()
{
 scanf("%d%d",&n,&m);
 for(i=1;i<=n;i++) scanf("%d",&a[i]);
 for(i=1;i<=m;i++)
   {
    scanf("%d",&x);
    int li=1,ri=n,mi;
    while(li<=ri)
      {
	   mi=li+(ri-li)/2;
	   if(x<=a[mi]) {ans=mi;ri=mi-1;}
	          else  li=mi+1;
	  } 
	 if(a[ans]==x) printf("%d ",ans);
	        else   printf("-1 ");
    }
 return 0;
}

hash插入函数

void insert()
{
	int hx=1;
	for(int i=0;i<re.size();i++)
	{
		hx=(hx*base+re[i])%mod;
	}
	for(int i=0;i<s[hx].size();i++)
	{
		if(s[hx][i]==re) return;
	}
	ans++;
	s[hx].push_back(re);
}

快速幂

long long pow(long long x,long long y,long long m)
{
	long long re=1;
	while(y>=1)
	{
		if(y&1) re=re*x%m;
		x=x*x%m;
		y>>=1;
	}
	return re;
}

线段树

inline void build(int i,int l,int r){//递归建树
    tree[i].l=l;tree[i].r=r;
    if(l==r){//如果这个节点是叶子节点
        tree[i].sum=input[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(i*2,l,mid);//分别构造左子树和右子树
    build(i*2+1,mid+1,r);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;//刚才我们发现的性质return ;
}
例子:求和
inline int search(int i,int l,int r){
    if(tree[i].l>=l && tree[i].r<=r)//如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
        return tree[i].sum;
    if(tree[i].r<l || tree[i].l>r)  return 0;//如果这个区间和目标区间毫不相干,返回0
    int s=0;
    if(tree[i*2].r>=l)  s+=search(i*2,l,r);//如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
    if(tree[i*2+1].l<=r)  s+=search(i*2+1,l,r);//如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
    return s;
}

区间修改

inline void add(int i,int l,int r,int k){
    if(tree[i].l>=l && tree[i].r<=r){//如果这个区间被完全包括在目标区间里面,讲这个区间标记k
        tree[i].sum+=k;
        return ;
    }
    if(tree[i*2].r>=l)
        add(i*2,l,r,k);
    if(tree[i*2+1].l<=r)
        add(i*2+1,l,r,k);
}

//单点查询
void search(int i,int dis){
    ans+=tree[i].sum;//一路加起来
    if(tree[i].l==tree[i].r)
        return ;
    if(dis<=tree[i*2].r)
        search(i*2,dis);
    if(dis>=tree[i*2+1].l)
        search(i*2+1,dis);
}

树状数组(简单版线段树)

int lowbit(int x)
{
	return x&(-x);
}//求二进制最低位1 
/*void add(int u,int v)//u表示位置,v表示加数 
{
	for(int i=u;i<=n;i+=lowbit(i))
	{
		c[i]+=v;
	}
	return;
}
void build()
{
	for(int i=1;i<=n;i++)
	{
		add(i,a[i]);
	}
	return;
}O(nlogn)build*/
void build()
{
	for(int i=1;i<=n;i++)
	{
		c[i]+=a[i];
		int fa=i+lowbit(i);
		if(fa<=n)
		{
			c[fa]+=c[i];
		}
	}
	return;
}//O(n)build

   单点修改
   
void change(int u,int v)//u is address,v is the number that add.
{
	for(int i=u;i<=n;i+=lowbit(i))
	{
		c[i]+=v;
	}
} 
int getsum(int m)//前缀和
{
	int ans=0;
	for(int i=m;i>0;i-=lowbit(i))
	{
		ans+=c[i];
	}
	return ans;
} 

转载自我的Luogu: https://www.luogu.com.cn/paste/5lsk97uh