模板整理2

动态规划

多重背包二进制优化

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu
Algorithm:
Date:2019.8.30
************************/

#include<bits/stdc++.h>

using namespace std;

const int maxn = 205;
const int maxk = 2e4 + 5;
int b[maxn],c[maxn],new_b[4005],new_c[4005],new_id[4005];
int dp[maxk],ans[maxn];
int n,k,cnt;
bool vis[3205][maxk];

void readdata(){
	read(n);
	for(int i = 1;i <= n; ++ i) read(b[i]);
	for(int i = 1;i <= n; ++ i) read(c[i]);
	read(k);
}

void work(){
	for(int i = 1;i <= n; ++ i){
		c[i] = min(c[i],k/b[i]);
		for(int j = 1;j <= c[i]; j <<= 1){
			new_b[++cnt] = b[i] * j;
			new_c[cnt] = j;
			new_id[cnt] = i;
			c[i] -= j;
		}
		
		if(c[i]){
			new_b[++cnt] = b[i] * c[i];
			new_c[cnt] = c[i];
			new_id[cnt] = i;
			c[i] = 0;
		}
	} 
	memset(dp,0x3f3f3f3f,sizeof(dp));
	dp[0] = 0;
	for(int i = 1;i <= cnt; ++ i){
		for(int j = k;j >= new_b[i]; -- j){
			if(dp[j] > dp[j - new_b[i]] + new_c[i]){
				dp[j] = dp[j - new_b[i]] + new_c[i];
				vis[i][j] = 1;
			}
		}
	}
	
	put(dp[k]);
	putchar('\n');
	int i = cnt,j = k;
	while(i >= 1){
		if(vis[i][j])
			j = j - new_b[i],ans[new_id[i]] += new_c[i];
		--i;
	}
	for(int i = 1;i <= n; ++ i){
		put(ans[i]);
		putchar(' ');
	}
	
//	printf("\n%lf",(double)sizeof(vis)/1024/1024);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

动规矩阵优化

GTExam

/**********************
User:Mandy
Language:c++
Problem:luogu 3193 GT Exam 
**********************/
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
 
const int maxm = 25;

ll n,m,mod;
char a[maxm];
int fail[maxm];
int g[maxm][maxm];
int  f[maxm];

void readdata(){
	read(n);read(m);read(mod);
	scanf("%s",a+1);
}

void multi(){
	int c[maxm];
	memset(c,0,sizeof(c));
	for(int i = 0;i < m; ++ i)
		for(int j = 0;j < m; ++ j)
			c[j] = (c[j] + f[i] * g[i][j] % mod) % mod;
	memcpy(f,c,sizeof(c));
}

void mulself(){
	int c[maxm][maxm];
	memset(c,0,sizeof(c));
	for(int i = 0;i < m; ++ i)
		for(int k = 0;k < m; ++ k)
			for(int j = 0;j < m; ++ j)//不能算匹配成功的 
				c[i][j] = (c[i][j] + g[i][k] * g[k][j] % mod) % mod;
	memcpy(g,c,sizeof(c));
}

void get_g(){//求出在i位置后面加一个字符使匹配长度变为j的方案数g[i][j] 
	int j = 0;
	for(int i = 1;i < m; ++ i){
		while(j > 0 && a[j+1] != a[i+1]) j = fail[j];
		if(a[j+1] == a[i+1]) j++;
		fail[i+1] = j;
	}//KMP fail
	j = 0;
	for(int i = 0;i < m; ++ i){
		for(int k = 0;k <= 9; ++ k){
			j = i;
			while(j && a[j+1]!=(k^48)) j = fail[j];
			if(a[j+1] == (k^48)) j++;
			g[i][j] = (g[i][j] + 1) % mod;
			//0也是要计入的 
		}
	}
}

void work(){
	get_g();
	f[0] = 1;
	while(n) {
		if(n&1) multi();
		n >>= 1 ;
		mulself();
	}
	int ans = 0;
	for(int i = 0;i < m; ++ i) ans = (ans + f[i]) % mod;
	put(ans%mod);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

四边形不等式

/***************************
User:Mandy
Language:c++
Problem:stone 
***************************/
#include<bits/stdc++.h>

using namespace std;

const int maxn = 2005;

int sum[maxn];
int fa[maxn][maxn],fi[maxn][maxn];
int s[maxn][maxn];

int n;

void readdata(){
	read(n);
	for(int i = 1;i <= n; ++ i){
		read(sum[i]);
		sum[i + n] = sum[i];
	}
	n <<= 1;n--; 
	for(int i = 1;i <= n; ++ i){
		sum[i] += sum[i - 1];
		s[i][i] = i;		
	}
}

void work(){
	for(int i = n;i >= 1; -- i)
		for(int j = i+1;j <= n; ++ j){
			int tmp = 0x3f3f3f3f;
			int id = 0;
			fa[i][j] = max(fa[i][j-1],fa[i+1][j]) + sum[j] - sum[i-1];
			for(int k = s[i][j-1];k <= s[i+1][j];++ k){
				int cur = fi[i][k] + fi[k+1][j] + sum[j] - sum[i-1];
				if(cur < tmp){
					tmp = cur;
					id = k;
				}
			}
			fi[i][j] = tmp;
			s[i][j] = id;
		}
		
	int ansmax = 0,ansmin = 0x3f3f3f3f;
	n=(n+1)>>1;
	for(int i = 1;i <= n; ++ i){
		ansmax = max(ansmax,fa[i][i+n-1]);
		ansmin = min(ansmin,fi[i][i+n-1]);
	}
	printf("%d\n%d",ansmin,ansmax);
}

int main(){
	readdata();
	work();
	return 0;
}

斜率优化

/*************************
User:Mandy.H.Y
Language:c++
Problem:luogu3195 Toy
Algorithm:斜率优化
Date:2019.7.24
Scores: 
*************************/

#include<bits/stdc++.h>

using namespace std;

const int maxn = 5e4 + 5;

int n,L;
long long a[maxn],b[maxn],dp[maxn];
long long l,r,q[maxn << 1];


void readdata(){
	read(n);read(L);
	++L;
	for(int i = 1;i <= n; ++ i){
		long long x;read(x);
		a[i] = a[i - 1] + x;
	}
	for(int i = 1;i <= n; ++ i){
		a[i] += i;
		b[i] = a[i] + L;
	}//i不能累加,要单独加上去 
	
}

void work(){
	l = r = 1;//保证队列中至少两个元素 
	b[0] = L;//初始化 
	for(int i = 1;i <= n; ++ i){
		long long k = a[i] << 1;
		while(l < r && 
			 (b[q[l + 1]] * b[q[l + 1]] + dp[q[l + 1]] - b[q[l]] * b[q[l]] - dp[q[l]]) <=
			 (k * (b[q[l + 1]] - b[q[l]])))
			 ++ l;
		
		dp[i] = dp[q[l]] + (a[i] - b[q[l]]) * (a[i] - b[q[l]]);
		
		while(l < r &&
			 (dp[q[r]] + b[q[r]] * b[q[r]] - dp[q[r - 1]] - b[q[r - 1]] * b[q[r - 1]]) * (b[i] - b[q[r]]) >=
			 (dp[i] + b[i] * b[i] - dp[q[r]] - b[q[r]] * b[q[r]]) * (b[q[r]] - b[q[r - 1]]))
			 -- r;
		q[++ r] = i;
	}
	put(dp[n]);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

树的最长链

#include<bits/stdc++.h>

using namespace std;

const int maxn = 2e5 + 5;

int n,m,size,first[maxn],cnt,mdis;
int father[maxn],ans[maxn];
int tmp[maxn][2];

struct Edge{
	int v,nt;
}edge[maxn << 1];

template<class T>inline void read(T &x){
	x = 0;bool flag = 0;char ch = getchar();
	while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag) x = -x;
}

void eadd(int u,int v){
	edge[++size].v = v;
	edge[size].nt = first[u];
	first[u] = size;
}

void readdata(){
	read(n);
	for(int i = 1;i < n; ++ i){
		int u,v;read(u);read(v);
		eadd(u+1,v+1);eadd(v+1,u+1);
	}
}

void dfs(int u,int fa){
	for(int i = first[u];i;i = edge[i].nt){
		int v = edge[i].v;
		if(v == fa) continue;
		dfs(v,u);
		if(tmp[v][0]+1>=tmp[u][0]) tmp[u][1] = tmp[u][0],tmp[u][0] = tmp[v][0]+1;
		else if(tmp[v][0]+1>tmp[u][1]) tmp[u][1] = tmp[v][0]+1;
	}
	mdis = max(tmp[u][0]+tmp[u][1],mdis);
}

void dfs1(int u,int fa,int dis){
	for(int i = first[u];i;i = edge[i].nt){
		int v = edge[i].v;
		if(v == fa) continue;
		if(tmp[v][0]+1==tmp[u][0]) dfs1(v,u,max(dis,tmp[u][1])+1);//注意+1的位置 
		else dfs1(v,u,max(dis,tmp[u][0])+1);
	}
	int cur = max(dis,tmp[u][1])+tmp[u][0];
	if(cur == mdis) ans[++cnt] = u;
}

void work(){
	dfs(1,0);
	dfs1(1,0,0);
	sort(ans+1,ans+1+cnt);
	for(int i = 1;i <= cnt; ++ i) put(ans[i]-1),putchar('\n');
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

基环树Dp

骑士

/*********************
User:Mandy.H.Y
Language:c++
Problem:luogu2607
Algorithm: 
*********************/
//嗯,谁说多了一条边就只有一个环?
//可能多个连通块,多个环 
//这道题连单向边 
#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e6 + 5;

int n,size,first[maxn];
int r1,r2;
long long val[maxn];
long long dp[maxn][4];//long long 
bool vis[maxn],used[maxn];

struct Edge{
	int v,nt;
}edge[maxn<<1];

void eadd(int u,int v){
	edge[++size].v = v;
	edge[size].nt = first[u];
	first[u] = size;
}

void readdata(){
	read(n);
	for(int i = 1;i <= n; ++ i){
		read(val[i]);
		int u;read(u);
		eadd(u,i);
		//被讨厌的指向讨厌的 
		//单向边就好 
	}
}

void find_circle(int u){
	vis[u] = 1;//标记是否有环 
	
	for(int i = first[u];i;i = edge[i].nt){
		int v = edge[i].v;
		if(used[v]) continue;
		//走下去没环的 
		if(vis[v]){
			r1 = u;
			r2 = v;
			continue;
		}
		
		find_circle(v);
	}
	used[u] = 1;//标记连通块
	//如果放前面就直接跳走了 
	vis[u] = 0;//撤标记 
	//不然可能判错 
}

void dfs(int u,int rt){
	used[u] = 1;
	dp[u][0] = 0;
	dp[u][1] = val[u];
	for(int i = first[u];i;i = edge[i].nt){
		int v = edge[i].v;
		if(v == rt) continue;//重新走到根 
		dfs(v,rt);
		dp[u][0] += max(dp[v][0],dp[v][1]);
		dp[u][1] += dp[v][0];
	}
}

void work(){
	long long ans = 0;
	for(int i = 1;i <= n; ++ i){
		if(!used[i]){
			r1 = r2 = 0;
			long long cur = -100;
			find_circle(i);
			if((!r1) && (!r2)) continue;
			//没走到环的 
			//因为是有向边 
			dfs(r1,r1);
			cur = max(cur,dp[r1][0]);
			dfs(r2,r2);//因为r1与r2不共存,所以分别为根 
			cur = max(cur,dp[r2][0]);
			ans += cur;
		}
	}
	put(ans);
}

int main(){
//	file();
	readdata();
	work();
	return  0;
}

基环树直径

/**********************
User:Mandy.H.Y
Language:c++
Problem:
Algorithm:
**********************/
#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e6 + 5;

int n,first[maxn],size = 1,s[maxn << 1],tp,q[maxn],l,r;
struct Edge{
	int v,nt;
	long long w;
}edge[maxn << 1];
long long tmp[maxn][2],ans,cur,dis[maxn << 1],judge;
bool dfn[maxn],vis[maxn];
void eadd(int u,int v,int w){
	edge[ ++ size].v = v;
	edge[size].w = w;
	edge[size].nt = first[u];
	first[u] = size;
}

void readdata(){
	read(n);
	for(int i = 1;i <= n; ++ i){
		int v,w;read(v);read(w);
		eadd(i,v,w);eadd(v,i,w);
	}
}

bool get_circle(int u,int e){//用反向边判断,因为有可能有一条边连向父亲 
	if(dfn[u]) return 0;
	if(vis[u]) {dfn[u] = 1;dis[tp] = 0;s[tp++] = u;return 1;}//环开始 
	vis[u] = 1;
	for(int i = first[u];i;i = edge[i].nt){
		int v = edge[i].v;if(i == (e ^ 1)) continue;
		if(get_circle(v,i)) {
			if(u == s[0]) {dis[tp] = dis[tp - 1] + edge[i].w;judge = 1;return 1;}//环结束 
			if(!judge){dfn[u] = 1;dis[tp] = dis[tp - 1] + edge[i].w;s[tp++]=u;}
			return 1;
		}
	}
	return 0;
}

void get_chain(int u,int e){
	for(int i = first[u];i;i = edge[i].nt){
		int v = edge[i].v;if(i == (e ^ 1) || dfn[v]) continue;//不能又走到环上 
		get_chain(v,i);
		if(tmp[v][0] + edge[i].w >= tmp[u][0]){
			tmp[u][1] = tmp[u][0];tmp[u][0] = tmp[v][0] + edge[i].w;
		} else tmp[u][1] = max(tmp[u][1],tmp[v][0] + edge[i].w);
	}
	cur = max(cur,tmp[u][0] + tmp[u][1]);
	dfn[u] = 1;
}

long long get(int x){
	tp = 0;cur = 0;judge = 0;
	l = r = 0;
	get_circle(x,0);//找环 
	for(int i = 0;i < tp; ++ i)	get_chain(s[i],0),s[tp + i] = s[i]; 
	for(int i = tp + 1;i < (tp << 1); ++ i) dis[i] = dis[i - 1] + dis[i - tp] - dis[i - tp - 1];
	for(int i = 0;i < (tp << 1); ++ i){//单队 
		while(l < r && i - q[l] >= tp) l ++;
		if(l < r) cur = max(cur,dis[i] - dis[q[l]] + tmp[s[i]][0] + tmp[s[q[l]]][0]);
		while(l < r && tmp[s[i]][0]-tmp[s[q[r-1]]][0] >= dis[i] - dis[q[r-1]]) r -- ;
		q[r ++ ] = i; 
	}
	return cur;
}

void work(){
	for(int i = 1;i <= n; ++ i){
		if(!dfn[i]) {
			ans += get(i);
		}
	}
	put(ans);
}

int main(){
	readdata();
	work();
	return 0;
}

LCS

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu1439 LCS
Algorithm:
Date:2019.7.29
Scores: 
************************/
//他日若云月抛砚执笔,为我题扇面 
#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 5;

int n;
int a[maxn],b[maxn],t[maxn];


template<class T>inline void read(T &x){
	x = 0;bool flag = 0;char ch = getchar();
	while( ! isdigit(ch)) flag |= ch == '-',ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag) x = -x;
}

template<class T>void putch(const T x){
	if(x > 9) putch(x / 10);
	putchar(x % 10 | 48); 
}

template<class T>void put(const T x){
	if(x < 0) putchar('-'),putch(-x);
	else putch(x);
}
void readdata(){
	read(n);
	for(int i = 1;i <= n; ++ i) read(a[i]);
	for(int i = 1;i <= n; ++ i) read(b[i]);
}

int erfen(int l,int r,int x){
	while(l <= r){
		int mid = (l + r) >> 1;
		if(t[mid] <= x) l = mid + 1;
		else r = mid - 1;
	}
	
	return l;
}

void work(){
	
	for(int i = n;i >= 1; -- i) t[a[i]] = i;
	for(int i = 1;i <= n; ++ i) a[i] = t[b[i]];
	
	memset(t,0,sizeof(t));
	int len = 0;t[0] = -1;
	
	for(int i = 1;i <= n; ++ i){
		
		if(t[len] < a[i]) t[++len] = a[i];
		else{
			int pos = erfen(1,len,a[i]);
			t[pos] = a[i];
		} 
	}
	
	put(len);
	
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

最大字段和

/**********************
User:Mandy.H.Y
Language:c++
Problem:hdu max sum
Algorithm:
Scores:
**********************/
//hdu与poj都不能用万能头文件 
//#include<bits/stdc++.h>
#include<cstdio> 
#include<iomanip> 
#include<algorithm> 
#include<cmath> 

using namespace std;

const int maxn = 1e5 + 5;

int t,n,ans,ansl,ansr;
int a[maxn];
int l[maxn],r[maxn],sum[maxn];

void readdata(){
	read(n);
	for(int i = 1;i <= n; ++ i) read(a[i]);
}

void work(int j){
	ans = -2e9;
	sum[0] = -1;//初始化 
	for(int i = 1;i <= n;++ i){
		l[i] = i;r[i] = i;
		if(sum[i - 1] >= 0){//要输出第一个位置,需要sum[0]初始化为负 
			sum[i] = sum[i - 1] + a[i];
			l[i] = l[i - 1];
		}else sum[i] = a[i];
		
		if(ans < sum[i]){//要输出第一个位置 
			ans = sum[i];
			ansl = l[i];
			ansr = r[i];
		}
	}
	
	printf("Case %d:\n",j);
	put(ans);putchar(' ');
	put(ansl);putchar(' ');
	put(ansr);
	puts("");//一个空格和换行符引发的血案 
	if(j != t) puts("");//要空行 
}

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

最大子矩阵和

/************************
User:Mandy.H.Y
Language:c++
Problem:hdu
Algorithm: 
Date:2019.8.16
************************/

#include<cstdio>
#include<iomanip>
#include<cmath>
#include<algorithm>
#define Max(x,y) ((x)>(y)?(x):(y))

using namespace std;

const int maxn = 105;

int n;
int t[maxn];
int sum[maxn][maxn];

void readdata(){
	for(int i = 1;i <= n; ++ i)
		for(int j = 1;j <= n; ++ j){
			read(sum[i][j]);
			sum[i][j] += sum[i - 1][j];
		}
}

void work(){
	int ans = -2e9;
	for(int i = 1;i <= n; ++ i)
		for(int j = i;j <= n; ++ j){
			for(int k = 1;k <= n; ++ k){
				t[k] = sum[j][k] - sum[i - 1][k]; 
				if(t[k - 1] > 0) t[k] += t[k - 1];
				ans = Max(ans,t[k]);
			}
		}
		
	put(ans);
	puts("");
}

int main(){
//	file();
	while(~scanf("%d",&n)){
		readdata();
		work();
	}
	
	return 0;
}

最大子矩阵问题

枚举所有极大子矩形

/*************************
User:Mandy.H.Y
Language:c++
Problem:luogu1578
Algorithm: 
Date: 2019.8.14 
*************************/

#include<bits/stdc++.h>
#define Max(x,y) ((x) > (y) ? (x) : (y))
#define Min(x,y) ((x) < (y) ? (x) : (y))

using namespace std;

const int maxn = 3e4 + 5;
const int maxp = 5e3 + 10;

typedef long long ll;

int n,m,p;
long long ans = 0;

struct Node{
	int x,y;
}node[maxp];

void readdata(){

	read(n);read(m);read(p);

	for(int i = 1;i <= p;++ i) read(node[i].x),read(node[i].y);
	node[++p].x = 0;node[p].y = 0;
	node[++p].x = 0;node[p].y = m;
	node[++p].x = n;node[p].y = 0;
	node[++p].x = n;node[p].y = m;//防止超出矩形边界 
}

bool cmp1(const Node &a,const Node &b){
	return a.y < b.y;
}

bool cmp2(const Node &a,const Node &b){
	return a.x < b.x;
}

void work(){
	
	sort(node + 1,node + p + 1,cmp1);
	
	for(int i = 1;i <= p; ++ i){
		
		int up = 0,down = n,l = m - node[i].y;
		
		for(int j = i + 1;j <= p; ++ j){//右边在矩形边界与右边是障碍物的情况 
			//down - up不是up -  down 
			if(node[j].x <= up || node[j].x >= down) continue;//在上下界之外不考虑 
			if((down - up) * l <= ans) break;//剪枝 
			ans = max(ans,(down - up) * ((long long)node[j].y - node[i].y));//作为右边界 
			if(node[j].x == node[i].x) break;//如果横坐标相等,后来就都是0 
			
			if(node[j].x < node[i].x) up = node[j].x;
			else down = node[j].x;
		}
		
		up = 0,down = n,l = node[i].y;
		
		for(int j = i - 1;j >= 1; -- j){//左边在矩形边界上 

			if(node[j].x <= up || node[j].x >= down) continue;
			if((down - up) * l <= ans) break;
			ans = max(ans,(down - up) * ((long long)node[i].y - node[j].y));
			if(node[j].x == node[i].x) break;
			if(node[j].x < node[i].x) up = node[j].x;
			else down = node[j].x;
			
		}
	}
	
	sort(node + 1,node + p + 1,cmp2);
	
	for(int i = 2;i <= p; ++ i) ans = max(ans,(long long)m * (node[i].x - node[i - 1].x));
	//考虑左右都在矩形边界上的情况 
	put(ans); 
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

悬线法

/**********************
User:Mandy.H.Y
Language:
Problem:4147
Algorithm:
**********************/

#include<bits/stdc++.h>

using namespace std;

const int maxn = 1005;

int n,m,ans = 0;
char a[maxn][maxn];
int _left[maxn][maxn];
int _right[maxn][maxn];
int _over[maxn][maxn];

void readdata(){
	read(n);read(m);
	for(int i = 1;i <= n; ++ i){
		for(int j = 1;j <= m; ++ j){
			
			char c = getchar();
			while(c != 'R' && c != 'F') c = getchar();
			a[i][j] = c;
			if(c == 'R') continue;//题目要求必须等于'F' 
			_left[i][j] = _right[i][j] = j;
			_over[i][j] = 1;
		}
	}
}

void work(){
	
	for(int i = 1;i <= n; ++ i)
		for(int j = 2;j <= m; ++ j)
			if(a[i][j] == a[i][j - 1] && a[i][j] == 'F')
				_left[i][j] = _left[i][j - 1];
	
	for(int i = 1;i <= n; ++ i)
		for(int j = m - 1;j >= 1; -- j)
			if(a[i][j] == a[i][j + 1] && a[i][j] == 'F')//注意倒序 + 1 
				_right[i][j] = _right[i][j + 1];
				
	for(int i = 1;i <= n; ++ i){
		for(int j = 1;j <= m; ++ j){
			if(i > 1 && a[i][j] == a[i - 1][j] && a[i][j] == 'F'){//注意边界 
				_left[i][j] = max(_left[i][j],_left[i - 1][j]);
				_right[i][j] = min(_right[i][j],_right[i - 1][j]);
				_over[i][j] = _over[i - 1][j] + 1;
			}
			
			int len1 = _right[i][j] - _left[i][j] + 1;
			
			ans = max(ans,_over[i][j] * len1);
		}
	}
	
	put(ans * 3);

}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

数位DP

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu2602
Algorithm:数位 DP DFS 
************************/
#include<bits/stdc++.h>
//这道题如果从 0 开始的话还要加个特判 
using namespace std;

const int maxn=35;

typedef long long LL;
LL a,b;
LL num[15],dp[20][20];

void file(){
	freopen("count.in","r",stdin);
}

LL dfs(LL dig,int limit,int lead,LL sum,LL pos){
	//pos 位数
	//dig 要找的数字
	//limit 这一位是否有限制 
	//lead 是否有前导0  如果lead==0 那么有前导0
	//sum : 搜到pos位时出现了多少个dig 
	if(!pos) return sum;
	
	if(!limit && lead && dp[pos][sum]!=-1) return dp[pos][sum];
	//没上限且没有前导0 
	//主要防止0算错   eg.0000 0010 
	LL ans=0;
	
	int top=limit ? num[pos] : 9;
	
	for(int i=0;i<=top;++i)
		ans+=dfs(dig,limit&&i==top,i||lead,sum+((lead||i)&&(i==dig)),pos-1);
	
	if(!limit && lead) dp[pos][sum]=ans;
	
	return ans;
}

LL work(LL dig,LL r){
	memset(num,0,sizeof(num));
	memset(dp,-1,sizeof(dp));
	LL x=r;
	
	if(!x) num[0]=1;
	while(x){
		++num[0];
		num[num[0]]=x%10;
		x/=10;
	}
	
	return dfs(dig,1,0,0,num[0]);
}

int main(){
//	file();
	read(a);read(b);
	for(LL i=0;i<=9;++i){
		put(work(i,b)-work(i,a-1));
		putchar(' ');
	}
	return 0;
}

二次扫描与换根

意会意会

数学

乘法逆元

exgcd

#include<bits/stdc++.h>

using namespace std;

void exgcd(int a,int b,int &k,int &p){
	if(!b) k = 1,p = 0;
	else exgcd(b,a % b,p,k),p -= a/b*k;
}

int main(){
	int a,b;
	scanf("%d%d",&a,&b);
	int k,p;
	exgcd(a,b,k,p);
	if(k <= 0) k += ((-k)/b+1)*b;
	printf("%d",k); 
	return 0;
}

ksm


#include<bits/stdc++.h>

using namespace std;

const int mod = 19260817;

long long a,b;

long long qk_p(long long b,long long p){
	long long ans = 1;
	while(p){
		if(p&1) ans = ans*b%mod;
		p>>=1;
		b = b * b%mod ;
	}
	return ans;
}

long long inv(long long x){
	return qk_p(x,mod-2);
}

int main(){
	read(a);read(b);
	if(!b){
		puts("Angry!");
		return 0;
	}
	printf("%lld",a*inv(b)%mod);
	return 0;
}

阶乘逆元

#include<bits/stdc++.h>

using namespace std;

const int maxn = 2005;
const int mod = 10007;

long long n,m,k,a,b;
long long fact[maxn],inv[maxn];

void readdata(){
	read(a);read(b);read(k);read(n);read(m);
} 

long long ksm(long long x,long long p){
	long long ans = 1;
	while(p){
		if(p & 1) ans = ans * x % mod;
		p >>= 1;
		x = x * x % mod;
	}
	return ans;
}

long long Inv(long long x){
	return ksm(x,mod-2);
}

void work(){
	fact[1] = 1;
	for(int i = 2;i <= k; ++ i) fact[i] = fact[i - 1] * i % mod;
	inv[k] = Inv(fact[k]);
	for(int i = k - 1;i >= 1; -- i) inv[i] = inv[i + 1] * (i + 1) % mod;//阶乘逆元 
	long long ansa = ksm(a,n);
	long long ansb = ksm(b,m);
	put(fact[k]*inv[n]%mod*inv[k-n]%mod*ansa%mod*ansb%mod);
}

int main(){
	readdata();
	work();
	return 0;
}

线性求逆元

#include<bits/stdc++.h>

using namespace std;

const int maxn = 3e6 + 5;

int n,p;
int inv[maxn];
int main(){
	scanf("%d%d",&n,&p);
	inv[1] = 1;
	for(int i = 2;i <= n; ++ i){
		inv[i] = (p-p/i)*(long long)inv[p%i]%p;
	}
	for(int i =1;i <= n; ++ i){
		put(inv[i]);
		putchar('\n');
	}
	return 0;
}
//给定n个数,求逆元 
//思路1:把乘逆元转化为除,通分,前后缀积 
//思路2:用类似阶乘逆元的方法,将这n个数相乘 
//没说p一定是质数,用exgcd比较好 
//不要把函数类型名写错了 

#include<bits/stdc++.h>

using namespace std;

const int maxn = 5e6 + 5;
const long long p = 1000000007ll;
long long n;

long long k = 998244353ll;
long long a[maxn],s[maxn],s1=1ll,ans = 0ll,sk = 1ll;

char *TT,*mo,but[(1 << 18) + 2];
#define getchar() ((TT == mo && (mo = ((TT = but) + fread(but,1,1<<18,stdin)),TT == mo)) ? -1 : *TT++)
template<class T>inline void read(T &x){
	x = 0;char ch = getchar();bool flag = 0;
	while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag) x = -x;
} 

void exgcd(long long x,long long P,long long &c,long long &d){
	if(!P) c = 1,d = 0;
	else exgcd(P,x%P,d,c),d-=x/P*c;
}
/*
void exgcd(long long a,long long b)
{
	if(a==1&&b==0)
	{
		c=1;d=0;
		return;
	}
	exgcd(b,a%b);
	long long x1=c;
	c=d;
	d=x1-(a/b)*c;
	return;
}*/
long long qk_p(long long x,long long P){
	long long base = 1;
	while(P){
		if(P&1) base = base*x%p;
		P>>=1;
		x = x*x%p;
	}
	return base;
}

int main(){
//	freopen("1.in","r",stdin);
	read(n);
	s[0]=1ll;
	for(int i = 1;i <= n; ++ i) read(a[i]),s[i] = s[i-1]*a[i]%p;
	long long c=0ll,d=0ll;
	exgcd(s[n],p,c,d);
	c = (c % p+p)%p;
//	long long c = qk_p(s[n],p-2);
	for(int i = n;i >= 1; -- i){
		ans = (ans + c*s[i-1]%p*sk)%p;
		//卡常少一个mod 
		c=c*a[i]%p;
		sk = sk*k%p;
	}
	printf("%lld",ans); 
	return 0;
}
/*

int main(){
//	freopen("in.txt","r",stdin);
	read(n);
	s[0]=1;
	for(int i = 1;i <= n; ++ i) read(a[i]),s[i] = s[i-1]*a[i]%p;
	for(int i = n;i >= 1; -- i){
		ans = (ans + s[i-1]*s1%p*sk)%p;
		s1 = s1*a[i]%p;
		sk = sk*k%p;
	}
	long long c,d;
	exgcd(s[n],p,c,d);
	if(c <= 0) c+=((-c)/p+1)*p;
	c%=p;
	printf("%lld",ans*c%p); 
	return 0;
}
*/

组合数

luogu3807

Lucas

#define int long long
int t,n,m,p,f[100005];
int pow(int x,int y,int p){ //快速幂
    x%=p;
    int ans=1;
    for(int i=y;i;i>>=1,x=x*x%p) if(i&1) ans=ans*x%p;
    return ans;
}
int C(int n,int m,int p){ //求组合数
    if(m>n) return 0;
    return ((f[n]*pow(f[m],p-2,p))%p*pow(f[n-m],p-2,p)%p);
}
int lucas(int n,int m,int p){ //Lucas定理
    if(!m) return 1;
    return C(n%p,m%p,p)*lucas(n/p,m/p,p)%p;
}
signed main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&m,&p);
        f[0]=1;
        for(int i=1;i<=p;i++)
            f[i]=(f[i-1]*i)%p;
        printf("%lld\n",lucas(n+m,m,p));
    }
}

欧拉定理

a^b mod p

#include<bits/stdc++.h>

using namespace std;

const int maxn = 2e5 + 5;

typedef long long ll;

long long a,m,b,pm;

template<class T>inline void get(T &x){
	x = 0;bool flag = 0;char ch = getchar();
	while( ! isdigit(ch)) ch = getchar();
	while(isdigit(ch)) {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		flag |= x >= pm ;
		x %= pm;
		ch = getchar();
	}
	if(flag)x+=pm;
}

ll phi(ll n){
	ll ans = n,x=(ll)sqrt(n);
	for(int i = 2;i <= x; ++ i){
		if( ! (n % i)){
			ans = ans / i * (i - 1);
			while( ! (n % i)) n /= i;
		}
	}
	
	if(n > 1) ans = ans / n * (n - 1);
	return ans;
}

ll fastpower(ll a,ll b,ll c){
	if( ! b) return 1 % c;
	
	int ans = 1;
	
	while(b){
		if(b & 1) ans = ans * a % c;
		a = a * a % c;
		b >>= 1;
		
	}
	
	return ans;
}
 
int main(){
	
	read(a);read(m);
	pm = phi(m);
	get(b);
	ll ans = fastpower(a,b,m);
	put(ans);
	return 0;
} 

矩乘

/********************
User:Mandy.H.Y
Language:c++
Problem:loj 
********************/ 

//#include<bits/stdc++.h>
#include<stdio.h>
#include<cstring> 
#include<iomanip> 
#include<cmath>

using namespace std;

long long n,m;
int p[2][2];
int f[2];

void mulself(int a[2][2]){
	int c[2][2]={{0,0},{0,0}};//初始化 
	for(int i  = 0;i < 2 ; ++ i)
		for(int k  = 0;k < 2 ; ++ k)
			for(int j  = 0;j < 2 ; ++ j)
				c[i][j] = (c[i][j] + (long long)a[i][k] * a[k][j] % m) %  m; 
	memcpy(a,c,sizeof(c));
}

void multi(int a[2],int c[2][2]){
	int g[2]={0,0};
	for(int i = 0;i < 2; ++ i)
		for(int j = 0;j < 2; ++ j)
			g[j] = (g[j] + (long long)a[i] * c[i][j] % m ) % m;
	memcpy(a,g,sizeof(g));
}

void qk_p(long long k) {
	
	while(k) {
		if(k&1) multi(f,p);
		k>>=1;
		mulself(p);
	} 
}

int main(){
	read(n);
	while(n != -1){
		if(!n){//注意n可否为0; 
			puts("0");
			read(n); 
			continue;
		}
		f[0]=1;f[1]=0;
		p[0][0] = 1;p[0][1] = 1;
		p[1][0] = 1;p[1][1] = 0;
		
		m = 1e4;
		qk_p(n-1);
		put(f[0]);
		puts("");
		read(n);
	}
	return 0;
}

矩乘快速幂依托于结合律
广义矩乘:
求S到T刚好经过N条边的最短路

//矩乘快速幂用的是矩乘的结合律
//所以满足结合律的矩阵运算就可以用矩乘快速幂,广义矩乘
 

#include<bits/stdc++.h>

using namespace std;

const int maxn = 205;

int n,t,s,e,tot,cnt,size;
long long g[maxn][maxn];
long long a[maxn][maxn];

struct Tmp{
	int u,id;
}tmp[maxn];

struct Edge{
	int u,v,w;
}edge[maxn];
bool cmp(const Tmp &a,const Tmp &b){
	return a.u<b.u;
}

void readdata(){
	read(n);read(t);read(s);read(e);
	for(int i = 1;i <= t; ++ i){
		read(edge[i].w);
		read(tmp[++tot].u);tmp[tot].id = i;
		read(tmp[++tot].u);tmp[tot].id = i;
	}
	sort(tmp+1,tmp+tot+1,cmp);cnt = 0;
	for(int i = 1;i <= tot; ++ i) {
		if(tmp[i].u!=tmp[i-1].u){
			if(!edge[tmp[i].id].u) edge[tmp[i].id].u = ++cnt;
			else edge[tmp[i].id].v = ++cnt;
		}else{
			if(!edge[tmp[i].id].u) edge[tmp[i].id].u = cnt;
			else edge[tmp[i].id].v = cnt;
		}
		if(tmp[i].u == s) s = cnt;
		if(tmp[i].u == e) e = cnt;
	}
	memset(g,0x3f3f3f3f,sizeof(g));
//	for(int i = 1;i <= cnt; ++ i) g[i][i] = 0;
//不能给自己到自己赋0,本题与经过的边数有关,
//赋为0相当于给自己加了一条0边
//答案会出错 
	for(int i = 1;i <= t; ++ i){
		g[edge[i].u][edge[i].v] = min((long long)edge[i].w,g[edge[i].u][edge[i].v]);
		g[edge[i].v][edge[i].u] = g[edge[i].u][edge[i].v];
	}
}

void mulself(){
	long long c[maxn][maxn];
	memset(c,0x3f3f3f3f,sizeof(c));
	for(int k = 1;k <= cnt; ++ k)
		for(int i = 1;i <= cnt; ++ i)
			for(int j = 1;j <= cnt; ++ j)
				c[i][j] = min(c[i][j],g[i][k]+g[k][j]);
	memcpy(g,c,sizeof(c));
}

void mul(){
	long long c[maxn][maxn];
	memset(c,0x3f3f3f3f,sizeof(c));
	for(int k = 1;k <= cnt; ++ k)
		for(int i = 1;i <= cnt; ++ i)
			for(int j = 1;j <= cnt; ++ j)
				c[i][j] = min(c[i][j],a[i][k]+g[k][j]);
	memcpy(a,c,sizeof(c));
}

void work(){
	memcpy(a,g,sizeof(g));
	n--;
	//不能给a[i][i]赋0,初值就这样,n记得-1
	//毕竟不是真正的矩阵乘法,单位矩阵应该不适用了 
	while(n) {
		if(n & 1) mul();
		n >>= 1;
		mulself();
	}
	
	put(a[s][e]);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

欧拉函数

/***********************
User:Mandy
Language:c++
Problem:Visible Points
Algorithm:
***********************/
//特殊点(1,0),(0,1),(1,1)
//(0,0)本身不算 

#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e3 + 5;

int n,cnt;
long long phi[maxn];
int prime[maxn];
bool vis[maxn];

template<class T>inline void read(T &x){
	x = 0;char ch = getchar();bool flag = 0;
	while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag) x = -x;
}

template<class T>void putch(const T x){if(x > 9) putch(x / 10);putchar(x % 10 | 48);}
template<class T>void put(const T x){if(x < 0) putchar('-'),putch(-x);else putch(x);}

void readdata(){
	read(n);
}

void work(int t){
	put(t);putchar(' ');
	put(n);putchar(' ');
	put(phi[n]*2+3);putchar('\n');
}

void get_Euler(){
	for(int i = 2;i <= 1000; ++ i){
		if(!vis[i]) prime[++cnt] = i,phi[i] = i - 1;
		for(int j = 1;j <= cnt && prime[j] * i <= 1000; ++ j){
			int now = prime[j] * i;vis[now] = 1;
			if(i % prime[j] == 0){
				phi[now] = phi[i] * prime[j];
				break;
			} else phi[now] = phi[i] * (prime[j] - 1);
		}
	}
}

int main(){
	int c;read(c);
	get_Euler();
	for(int i = 2;i <= 1000; ++ i) phi[i] += phi[i - 1];
	for(int i = 1;i <= c; ++ i){
		readdata();
		work(i);
	}
	return 0;
}

线性筛

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

算数基本定理

#include<bits/stdc++.h>

using namespace std;

const int maxn = 5e4 + 5;
const int mod = 9901;

long long a,b,ans;
long long p[maxn][2],cnt;

void readdata(){
	read(a);read(b);
}

long long ksm(long long x,long long P){
	long long ans = 1;
	while(P){
		if(P & 1) ans = ans * x % mod;
		P >>= 1;
		x = x * x % mod;
	}
	return ans;
}

long long inv(long long x){
	return ksm(x,mod - 2);
}

void work(){
	int x = sqrt(a);
	long long cur = a;
	if(a == 0) {//!!!!!!!!!!!!!!! 
		puts("0");
		return;
	}
	for(int i = 2;i <= x; ++ i){
		if(cur % i == 0) {
			p[++cnt][0] = i;
			while(cur % i == 0) cur /= i,p[cnt][1]++;
		}
	}
	if(cur > 1) p[++cnt][0] = cur,p[cnt][1] = 1;
	//等比数列,算数基本定理推论 
	ans = 1;//!!!!!!
	for(int i = 1;i <= cnt; ++ i){
		if((p[i][0] - 1) % mod != 0) cur = (ksm(p[i][0],p[i][1] * b + 1) - 1 + mod) * inv(p[i][0] - 1 + mod) % mod;
		else cur = (b * p[i][1] + 1) % mod;
		ans = ans * cur % mod;
	}
	put(ans);
}

int main(){
	readdata();
	work();
	return 0;
}

整除分块

/***************
User:Mandy 
Language:c++
Problem:AcWing 
Algorithm: 
***************/

#include<bits/stdc++.h>

using namespace std;

long long n,k;
long long ans = 0;

void readdata(){
	read(n);read(k);
	ans = n * k;
}

void work(){
	
	for(int i = 1,j;i <= min(k,n); i = j+1){
		j = min(k/(k/i),n);
		//j>k时k/i = 0;
		//j必须小于n 
		ans -= ((long long)k/i)*((long long)i+j)*((long long)j-i+1)/2;
	}
	put(ans);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

出栈sixways

//认真看,杜绝抄袭
//好好消化一下,这题很经典 
//记忆化搜索/递归 做法 
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll f[MAX_N][MAX_N];
ll dfs(int i,int j)
{
    if(f[i][j]) return f[i][j]; 
    if(i==0)return 1; //边界 
    if(j>0) f[i][j]+=dfs(i,j-1);
    f[i][j]+=dfs(i-1,j+1);
    return f[i][j];
}
int main()
{
    scanf("%d",&n);
    printf("%lld",dfs(n,0));
    return 0;
}

//递归转递推  递推做法 
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll f[MAX_N][MAX_N];
int main()
{
    scanf("%d",&n);
    for(int i=0;i<=n;i++)
    {
        f[0][i]=1;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=n;j++)
        {
            if(i==j)f[i][j]=f[i-1][j];
            else f[i][j]=f[i][j-1]+f[i-1][j];
        }
    }
    printf("%lld",f[n][n]);
    return 0;
}

//数论做法 卡特兰数
//公式1:
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll f[MAX_N];
int main()
{
    f[0]=f[1]=1;
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        for(int j=0;j<i;j++)
        {
            f[i]+=f[j]*f[i-j-1];
        }
    }
    printf("%lld",f[n]);
    return 0;
}

//公式2:
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll f[MAX_N];
int main()
{
    f[0]=f[1]=1;
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        f[i]+=f[i-1]*(4*i-2)/(i+1);
    }
    printf("%lld",f[n]);
    return 0;
}

//公式3:
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll c[MAX_N*2][MAX_N];
int main(){

    scanf("%d",&n);
    for(int i=1;i<=2*n;i++)
    {
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;j++)
        {
            c[i][j]=c[i-1][j]+c[i-1][j-1];
        }
    }
    printf("%lld",c[2*n][n]/(n+1));
    return 0;
}

//公式4: 
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll c[MAX_N*2][MAX_N];
int main(){

    scanf("%d",&n);
    for(int i=1;i<=2*n;i++)
    {
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;j++)
        {
            c[i][j]=c[i-1][j]+c[i-1][j-1];
        }
    }
    printf("%lld",c[2*n][n]-c[2*n][n-1]);
    return 0;
}

//高精/打表:
#include<iostream>
#include<cstdio> 
#include<cstring>
#define MAX_N 110
using namespace std;
int f[MAX_N][MAX_N],c[MAX_N];
inline int len(int a[]) 
{
    int i;
    for(i=60;i>=0;i--)//想要100个以上,这个i的范围要改 
    {
        if(a[i]!=0) break;
    }   
    return i;
}
inline void add(int a[],int b[],int w)//高精加法 
{
    int lena=len(a),lenb=len(b);
    for(int i=0;i<=max(lena,lenb);i++)
    {
        f[w][i]=a[i]+b[i];
    }
    for(int i=0;i<=max(lena,lenb)+1;i++) 
    {
        f[w][i+1]+=f[w][i]/10;
        f[w][i]%=10;
    }
}
inline void Catalan(int a[],int b[])//卡特兰 
{
    memset(c, 0, sizeof(c));
    int lena=len(a),lenb=len(b);
    for (int i=0;i<=lena;i++){
        for (int j=0;j<=lenb;j++) 
            c[i+j]+=a[i]*b[j];
    }      
    for (int i=0;i<=lena+lenb+1;i++)
    {
        c[i+1]+=c[i]/10;
        c[i]%=10;
    }
}
int main() 
{
    //int k;
    freopen("Catalan.txt","w"stdin);//文件操作; 
    f[0][0]=f[1][0]=1;
    for (int i=2;i<=100;i++)//同理,要多输出几个i就等于几 
    {
        for (int j=0;j<i;j++) 
        {
            Catalan(f[j], f[i-j-1]);
            add(f[i],c,i);
        }
    }
    for(int i=1;i<=100;i++)//输出 卡特兰数 1-100,范围同上,要输出几个自己改 
    {
        for (int j=len(f[i]);j>=0;j--)
        {
            //printf("%d",f[i][j]);
            putchar((char)f[i][j]+'0');//比printf稍快?  
        }
        printf("\n");
    }
    return 0;
}

01规划

最优比率生成树

/********************
User:Mandy
Language:c++
Problem:
********************/
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef double dou;

const int maxn = 1005;
const dou esp = 1e-8;

int t,n,size;
double d[maxn];
int from[maxn];
double dis[maxn][maxn],cost[maxn][maxn];

struct Node{
    int x,y,z;
}node[maxn];

void readdata(){
    for(int i = 1;i <= n; ++ i){
        read(node[i].x);
        read(node[i].y);
        read(node[i].z);
    } 
}

double dist(int i,int j){
    return sqrt(((dou)node[i].x - node[j].x) * 
                ((dou)node[i].x - node[j].x) + 
                ((dou)node[i].y - node[j].y) * 
                ((dou)node[i].y - node[j].y));
}

void init(){
    for(int i = 1;i <= n; ++ i){
        for(int j = i;j <= n; ++ j){
            dis[i][j] = dist(i,j);
            dis[j][i] = dis[i][j];
            cost[i][j] = abs((double)node[i].z - node[j].z);
            cost[j][i] = cost[i][j];
        }
    }
}

double Prim(double r){
    int cnt = 1;t = 0;double c = 0,D = 0;
    for(int i = 1;i <= n;++i) 
        d[i] = cost[1][i]-r*dis[1][i],from[i] = 1;
    d[1] = -2e9;
    for(int j = 1;j <= n; ++ j){
        double cur = 2e9;int id = 0;
        for(int i = 1;i <= n; ++ i){
            if(d[i]!=-2e9 && d[i] < cur){
                cur = d[i];    id = i;
            }
        }
        t += cur;cnt++;d[id] = -2e9;
        c += cost[from[id]][id];
        D += dis[from[id]][id];
        if(cnt == n) break;
        for(int i = 1;i <= n; ++ i) {
            if(d[i] > cost[id][i]-r*dis[id][i]){
                d[i] = cost[id][i]-r*dis[id][i];
                from[i] = id;
            }
        }
    }
    if(t < esp) return c/D;
    else return -2e9;
}

void work(){
    double ans = (double)7;
    while(1){
        double R = Prim(ans);
        if(R == -2e9||fabs(ans - R) <= esp) break;//fabs!!
        //直接判断是否相等就好,r不会等于-2e9 
        ans = R;
    }
    printf("%.3lf\n",ans);
}

int main(){
//    file();
    while(~scanf("%d",&n)){
        if(!n) break;
        size = 0;
        readdata();
        init();
        work();
    }
    return 0;
}

Gauss

/*************************
User:Mandy.H.Y
Language:c++
Problem: luogu3389
Algorithm:Gauss
*************************/

#include<bits/stdc++.h>

using namespace std;

const int maxn=105;
const double eps=1e-8;

int n;
double a[maxn][maxn];

void readdata()
{
	read(n);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n+1;++j)
			scanf("%lf",&a[i][j]);
}

double fa(double b)
{
	if(b<0) return -b;
	else return b;
}

void work()
{
	for(int i=1;i<=n;++i){
		
		int maxi=i;
		for(int j=i+1;j<=n;++j)
			if(a[j][i]>a[maxi][i]) maxi=j;
		if(maxi!=i) swap(a[i],a[maxi]);
		
		if(fa(a[i][i])<eps){
			printf("No Solution");
			return;
		}
		
		double d=a[i][i];
		for(int j=i;j<=n+1;++j) a[i][j]/=d;
		
		for(int j=i+1;j<=n+1;++j){
			d=a[j][i];
			for(int k=i;k<=n+1;++k)
				a[j][k]-=a[i][k]*d;
		}
	}
	
	for(int i=n-1;i>=1;--i)
		for(int j=i+1;j<=n;++j)
			a[i][n+1]-=a[j][n+1]*a[i][j];
			
	for(int i=1;i<=n;++i) printf("%.2lf\n",a[i][n+1]);
}

int main()
{
//	docu();
	readdata();
	work();
	return 0;
}

/*************************
User:Mandy.H.Y
Language:c++
Problem: Gauss
Algorithm:Gauss
*************************/

#include <bits/stdc++.h>

using namespace std;

const int maxn = 25;
const double eps = 1e-8;

int n;
double a[maxn][maxn];
double b[maxn * maxn];
double x[maxn * maxn];

void readdata() {
	int cnt = 0;
	while (cin >> b[cnt])
		++ cnt;
	n = int(sqrt(cnt));
	for (int i = 0; i < n; ++i)
		for (int j = 0; j <= n; ++j) {
			a[i][j] = b[i * (n+1) + j];
		}
}

double fa(double b) {
	if (b < 0)
		return -b;
	else
		return b;
}

int Gauss(int m, int n) {
	int col = 0, k = 0;
	for (; k < m && col < n; ++k, ++col) {
		int r = k;
		for (int i = k + 1; i < m; ++i)
			if (fa(a[i][col]) > fabs(a[r][col]))
				r = i;
		if (fa(a[r][col]) < eps) {
			k--;
			continue;
		}
		if (r != k)
			for (int i = col; i <= n; ++i)
				swap(a[k][i], a[r][i]);
		for (int i = k + 1; i < m; ++i)
			if (fa(a[i][col]) > eps) {
				double t = a[i][col] / a[k][col];
				for (int j = col; j <= n; j++)
					a[i][j] -= a[k][j] * t;
				a[i][col] = 0;
			}
	}
	for (int i = k ; i < m ; ++i)
		if (fa(a[i][n]) > eps)
			return -1;
	if (k < n)
		return n - k;
	for (int i = n - 1; i >= 0; i--) {
		double temp = a[i][n];
		for (int j = i + 1; j < n; ++j)
			temp -= x[j] * a[i][j];
		x[i] = (temp / a[i][i]);
	}
	return 0;
}

void work() {
	/*
	for (int i = 1; i <= n; ++i) {

		int maxi = i;
		for (int j = i + 1; j <= n; ++j)
			if (a[j][i] > a[maxi][i])
				maxi = j;
		if (maxi != i)
			swap(a[i], a[maxi]);

		if (fa(a[i][i]) < eps) {
			if (fa(a[i][n + 1]) < eps)
				printf("No unique solution!");
			else
				printf("No solution!");
			return;
		}

		double d = a[i][i];
		for (int j = i; j <= n + 1; ++j)
			a[i][j] /= d;

		for (int j = i + 1; j <= n + 1; ++j) {
			d = a[j][i];
			for (int k = i; k <= n + 1; ++k)
				a[j][k] -= a[i][k] * d;
		}
	}

	for (int i = n - 1; i >= 1; --i)
		for (int j = i + 1; j <= n; ++j)
			a[i][n + 1] -= a[j][n + 1] * a[i][j];
	*/
	int num = Gauss(n, n);
	if (num == -1)
		printf("No solution!");
	else if (num)
		printf("No unique solution!");
	else
		for (int i = 0; i < n; ++i)
			printf("%.3lf ", x[i]);
}

int main() {
//	docu();
	readdata();
	work();
	return 0;
}


图论

差分约束

主要是建图,看看例题就好

SPFA

while(l != r){
		int u = q[l];
		l++;
		l %= mod;
		vis[u] = 0;
		if(cnt[u] > n) {
			puts("successful conspiracy");
			return;
		}
		int rr = (r-1+mod)%mod;
		if(l != r) if(dis[q[l]] < dis[q[rr]]) swap(q[l],q[rr]);
		for(int i = first[u];i;i = edge[i].nt){
			int v = edge[i].v;
			int w = edge[i].w;
			if(dis[v] < dis[u] + w){
				dis[v] = dis[u] + w;
				cnt[v] = cnt[u] + 1;
				if(!vis[v]){
					vis[v] = 1;
					q[r++] = v;
					r %= mod;
					int rr = (r-1+mod)%mod;
					if(l != r) if(dis[q[l]] < dis[q[rr]]) swap(q[l],q[rr]);
				}
			}
		}
	}

次短路就维护次大值
01bfs双端队列
动态加点


//动态加点  

#include<bits/stdc++.h>

using namespace std;

const int maxn = 5e4 + 5;
const int maxm = 1e5 + 5;

int n,m,size,first[maxn];
int dis[maxn],ans = 0x3f3f3f3f;
bool vis[maxn];
int q[maxn + 5],l,r;

struct Edge{
	int v,nt,w;
}edge[maxm << 1];

struct EE{
	int u,v,a,b;
	EE(int _u = 0,int _v = 0,int _a = 0,int _b = 0):u(_u),v(_v),a(_a),b(_b){}
}E[maxm << 1];
void eadd(int u,int v,int w){
	edge[++size].v = v;edge[size].w = w;
	edge[size].nt = first[u];first[u] = size;
}

void readdata() {
	read(n);read(m);
	for(int i = 1;i <= m; ++ i) {
		int u,v,a,b;read(u);read(v);read(a);read(b);
		E[i] = EE(u,v,a,b);
	}
}

bool cmp(const EE &x,const EE &y){
	return x.a<y.a;
}

void spfa(int s,int t){
	l = r = 0;q[r++]=s;q[r++]=t;
	vis[s] = 1;vis[t] = 1;
	while(l != r){
		int u = q[l];++l;l = l%maxn;vis[u] = 0;
		int rr=(r-1+maxn)%maxn;if(dis[q[rr]]<dis[q[l]]) swap(q[l],q[rr]);
		for(int i = first[u];i;i = edge[i].nt){
			int v = edge[i].v,w = edge[i].w;
			if(dis[v]>max(dis[u],w)){
				dis[v]=max(dis[u],w);
				if(!vis[v]){
					vis[v] = 1;q[r++] = v;r%=maxn;
					int rr=(r-1+maxn)%maxn;if(dis[q[rr]]<dis[q[l]]) swap(q[l],q[rr]);
				}
			}
		}
	}
}

void work(){
	sort(E+1,E+m+1,cmp);memset(dis,0x3f3f3f3f,sizeof(dis));
	dis[1] = 0;
	for(int i = 1;i <= m; ++ i){
		eadd(E[i].u,E[i].v,E[i].b);eadd(E[i].v,E[i].u,E[i].b);
		spfa(E[i].u,E[i].v);ans = min(ans,dis[n]+E[i].a);
	}
	if(ans<0x3f3f3f3f)put(ans);
	else puts("-1");
}

int main(){
	file();
	readdata();
	work();
	return 0;
}

分层图意会
负环


bool SPFA(int s){
	l = r = 0;
	q[r ++ ] = s;
	while(l != r){
		int u = q[l];
		l++;
		used[u] = 1;
		l = l % 1000;
		int rr = (r - 1 + 1000) % 1000;
		if(l != r) if(dis[q[l]] > dis[q[rr]]) swap(q[l],q[rr]);
		vis[u] = 0;
		if(cnt[u] > n) return 1;
		for(int i = first[u];i;i = edge[i].nt){
			int v = edge[i].v;
			int w = edge[i].w;
			if(dis[u] + w < dis [v]){
				dis[v] = dis[u] + w;
				cnt[v] = cnt[u] + 1;
				if(!vis[v]){
					q[r++] = v;
					vis[v] = 1;
					r %= 1000;
					int rr = (r - 1 + 1000) % 1000;
					if(l != r) if(dis[q[l]] > dis[q[rr]]) swap(q[l],q[rr]);
				}
			}
		}
	}
	return 0;
}

最短路计数


void work()
{
	queue<int>q;
	q.push(1);
	t[1]=1;
	memset(d,0x3f3f3f,sizeof(d));
	d[1]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		v[u]=0;
		for(int i=f[u];i!=0;i=e[i].nt)
		{
			int v1=e[i].v;
			if(d[v1]>d[u]+1)
			{
				t[v1]=t[u]%100003;
				d[v1]=d[u]+1;
				if(!v[v1])
				{
					v[v1]=1;
					q.push(v1);
				}
			}
			else if(d[v1]==d[u]+1)
			{
				t[v1]=(t[v1]+t[u])%100003;
				if(!v[v1])
				{
					v[v1]=1;
					q.push(v1);
				}
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		printf("%d\n",t[i]%100003);
	}
}

Dijkstra


void work()
{
    memset(d,0x3f3f3f3f,sizeof(d));
    d[s]=0;
    q.push(make_pair(0,s));
    while(!q.empty())
    {
        int u=q.top().second;q.pop();
        if(vis[u]) continue;vis[u]=1;
        for(int i=f[u];i;i=e[i].nt){
            int v=e[i].v,w=e[i].w;
            if(d[u]+w<d[v]){
                d[v]=d[u]+w;q.push(make_pair(d[v],v));
            }
        }
    }
}

Floyd

传递闭包

/*********************
User:Mandy
Language:c++
Problem:AcWing
Algorithm:
*********************/
//其实这道题正解是topsort 
//天知道我拿到这一道题第一反应是并查集 
// 二分 + 传递闭包
//这道题是真的坑,题目的意思是求最少的关系数确定的关系 
//也就是说求出最小的t使得不等式确定大小关系或者得出矛盾
//重点在优先求最小的t 
//其实可以从头到尾枚举,每一次做一遍floyd 
#include<bits/stdc++.h>

using namespace std;

const int maxn = 30;

int n,m;
char c[1005][5];
char s1[maxn];
bool g[maxn][maxn];

struct Cnt{
	int c,id;
}cnt[maxn];

void floyd(int r){
	memset(g,0,sizeof(g));
	
	for(int i = 1;i <= r; ++ i)
		g[c[i][0]-'A'][c[i][2]-'A'] = 1;
	
	for(int k = 0;k < n; ++ k)
		for(int i = 0;i < n; ++ i)
			for(int j = 0;j < n; ++ j)
				g[i][j] |= g[i][k] & g[k][j];
}

int check(){
	bool judge = 0;
	for(int i = 0;i < n; ++ i){
		for(int j = i;j < n; ++ j){
			if(g[i][j] == 1 && g[j][i] == 1){
				return 0;//有矛盾 优先 
			}
			if(i != j && g[i][j] == 0 && g[j][i] == 0){
				judge = 1;//不确定不优先,万一有矛盾呢 
			}
		}
	}
	if(judge) return 1;//无矛盾,不确定 
	else return 2;//无矛盾,确定 
}

bool cmp(const Cnt &x,const Cnt &y){
	return x.c>y.c; 
}

void work(){
	memset(s1,0,sizeof(s1)); 
	for(int i = 1;i <= m; ++ i)
		scanf("%s",c[i]);
	
	floyd(m);
	int judge = check();
	if(judge == 1) {
		puts("Sorted sequence cannot be determined.");
		return;//所有m个关系都不能确定,直接输出 
	}
	int l = 0,r = m,ans = m;
	while(l <= r){
		int mid = (l + r)>>1;
		floyd(mid);
		int x = check();
		if(x != 1) {//只要关系不是不确定的就可以 
			if(s1[0]==0 && x == 2){//关系确定,且还没求过序列 
				memset(cnt,0,sizeof(cnt));
				for(int i = 0;i < n; ++ i){
					for(int j = 0;j < n; ++ j)
						if(g[i][j]) cnt[i].c++;
					cnt[i].id = i;
				}
				sort(cnt,cnt+n,cmp);//按小于的数的个数排序 
				for(int i = 0;i<n; ++ i) s1[i] = cnt[i].id+'A';
			}
			judge = x,ans = mid,r = mid-1;
			//judge负责记录最小的t求出的关系是大小关系还是矛盾 
		}
		else l = mid+1;
	}
	if(judge == 0) printf("Inconsistency found after %d relations.\n",ans);
	else {
		
		printf("Sorted sequence determined after %d relations: %s.\n",ans,s1);
	}
}

int main(){
	while(~scanf("%d%d",&n,&m) && (n || m))
		work();
	return 0;
}

最短路

for(int k = 1;k <= n; ++ k)
		for(int  i = 1;i <= n; ++ i)
			for(int j = 1;j <= n; ++ j)
				if(dis[i][j] > dis[i][k] + dis[k][j])
					dis[i][j] = dis[i][k] + dis[k][j];

最小环

/*********************
User:Mandy
Language:c++
Problem:AcWing
Algorithm: 
*********************/
#include<cstdio> 
#include<algorithm> 
#include<iomanip> 
#include<cstring> 

using namespace std;

const int maxn = 105;

int n,m;
int g[maxn][maxn];
int a[maxn][maxn];
int pos[maxn][maxn];
int path[maxn],cnt = 0;

void readdata(){
	read(n);read(m);
	memset(g,0x3f3f3f3f,sizeof(g));//存初始边 
	memset(a,0x3f3f3f3f,sizeof(a));//存最短路 
	for(int i = 1;i <= n; ++ i) a[i][i] = 0;//记得赋初值 
	//杜绝了pos[i][i]有值的可能性 
	for(int i = 1; i <= m; ++ i){
		int u,v,l;read(u);read(v);read(l);
		g[u][v] = min(g[u][v],l);//有重边 
		g[v][u] = g[u][v];
		a[u][v] = a[v][u] = g[u][v];
	}
}

void get_path(int i,int j){
	int k = pos[i][j];
	if(!k) return;
	else{
		get_path(i,k);
		path[++cnt] = k;
		get_path(k,j);
	}
}

void work(){
	
	int ans = 0x3f3f3f3f;
	
	for(int k = 1;k <= n; ++ k){//枚举环上最大的点 
		
		for(int i = 1;i < k; ++ i){ 
			for(int j = i+1;j < k; ++ j){ //三个点两两不同 
				if((long long)a[i][j]+g[i][k]+g[k][j] < ans){//开long long 
					ans = a[i][j]+g[i][k]+g[k][j];
					cnt = 0;
					path[++cnt] = i;get_path(i,j);
					path[++cnt] = j;path[++cnt] = k;//k与i,j都直接相连 
				}
			}
		}
		
		for(int i = 1;i <= n; ++ i){
			for(int j = 1;j <= n; ++ j){
				if(a[i][j] > a[i][k] + a[k][j]){
					a[i][j] = a[i][k] + a[k][j];
					a[j][i] = a[i][j];
					pos[i][j] = k;pos[j][i] = k;
				}
			}
		}
	}
	if(cnt == 0){
		puts("No solution.");
		return;
	}
	int start = 0,cur = n+1;
	for(int i = 1;i <= cnt; ++ i){
		if(path[i]<cur) cur = path[i],start = i;
	}
	printf("%d ",path[start]);
	int i = start+1;if(i>cnt)i=1;
	while(i != start){
		printf("%d ",path[i]);
		i++;if(i>cnt)i=1;
	}
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

线段树优化建图

/*******************
User:Mandy
Language:c++
Problem:luogu
Algorithm:
*******************/

//线段树优化建图 
//听说无向图建一棵就好,有向图建两颗 

#include<bits/stdc++.h>
#define lson tree[now].ls,l,mid
#define rson tree[now].rs,mid+1,r

using namespace std;
typedef long long ll;
const int maxm = 2e6 + 5;
const int maxn = 2e5 + 5;
//大概一棵树就有nlogn条边,两棵树乘以二 

int n,q,size,tot,s,rootin,rootout;
int mu,mv,ml,mr,first[maxn << 1];
bool vis[maxn << 1];//注意数组大小 
long long mw,dis[maxn << 1];
struct Edge{
	int v,nt;
	long long w;
}edge[maxm << 1];
struct SegmentTree{
	int ls,rs;
}tree[maxn << 1]; 
template<class T>inline void read(T &x){
	x = 0;char ch = getchar();bool flag = 0;
	while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag) x = -x;
}

template<class T>void putch(const T x){if(x > 9) putch(x / 10);putchar(x % 10 | 48);}
template<class T>void put(const T x){if(x < 0) putchar('-'),putch(-x);else putch(x);}

void eadd(int u,int v,long long w){
	edge[++size].v = v;
	edge[size].w = w;
	edge[size].nt = first[u];
	first[u] = size;
}

void buildin(int &now,int l,int r){
	if(l == r) {now = l;return;}
	now = ++tot;
	int mid = (l + r) >> 1;
	buildin(lson);buildin(rson);
	eadd(now,tree[now].ls,0);eadd(now,tree[now].rs,0);
}

void buildout(int &now,int l,int r){
	if(l == r) {now = l;return;}
	now = ++tot;
	int mid = (l + r) >> 1;
	buildout(lson);buildout(rson);
	eadd(tree[now].ls,now,0);eadd(tree[now].rs,now,0);
}

void modifyin(int now,int l,int r,int x,int y){
	if(x <= l && r <= y) {eadd(mv,now,mw);return;}
	int mid = (l + r) >> 1;
	if(x <= mid) modifyin(lson,x,y);
	if(y > mid) modifyin(rson,x,y);
}

void modifyout(int now,int l,int r,int x,int y){
	if(x <= l && r <= y) {eadd(now,mv,mw);return;}
	int mid = (l + r) >> 1;
	if(x <= mid) modifyout(lson,x,y);
	if(y > mid) modifyout(rson,x,y);
}

void readdata(){
	read(n);read(q);read(s);
	tot = n;//注意初始化 
	buildin(rootin,1,n);buildout(rootout,1,n);
	for(int i = 1;i <= q; ++ i){
		int opt;read(opt);
		switch(opt){
			case 1:{read(mu);read(mv);read(mw);eadd(mu,mv,mw);break;}
			case 2:{read(mv);read(ml);read(mr);read(mw);modifyin(rootin,1,n,ml,mr);break;}
			case 3:{read(mv);read(ml);read(mr);read(mw);modifyout(rootout,1,n,ml,mr);break;}
		}
	}
}

void dijkstra(){
	typedef pair<long long,int>pir;
	priority_queue<pir,vector<pir>,greater<pir> >q;
	memset(dis,0x3f,sizeof(dis));
	
	q.push(make_pair(0ll,s));dis[s] = 0;
	
	while(!q.empty()){
		int u = q.top().second;q.pop();
		if(vis[u]) continue;vis[u] = 1;
		for(int i = first[u];i;i = edge[i].nt){
			int v = edge[i].v;long long w = edge[i].w;
			if(dis[v] > dis[u] + w){
				dis[v] = dis[u] + w;
				q.push(make_pair(dis[v],v));
			}
		}
	} 
}

void work(){	
	dijkstra();
	for(int i = 1;i <= n; ++ i){
		if(dis[i] < 4e17) put(dis[i]),putchar(' ');
		else put(-1),putchar(' ');
	}
}

int main(){
	readdata();
	work();
	return 0;
}

缩点

#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e4 + 5;
const int maxm = 5e4 + 5;

int n,m,size,first[maxn],tot,tp,cnt;
int low[maxn],dfn[maxn],s[maxn],a[maxn];
int father[maxn],d[maxn];

bool vis[maxn];

struct Edge{
	int u,v,nt;
}edge[maxm];


//char *TT,*mo,but[(1 << 15) + 2];
//#define getchar() ((TT == mo && (mo = ((TT = but) + fread(but,1,1 << 15,stdin)),TT == mo)) ? -1 : *TT++)
template<class T>inline void read(T &x){
	x = 0;char ch = getchar();bool flag = 0;
	while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); 
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag) x = -x; 
}

template<class T>void putch(const T x){
	if(x > 9) putch(x / 10);
	putchar(x % 10 | 48);
}

template<class T>void put(const T x){
	if(x < 0) putchar('-'),putch(-x);
	else putch(x);
}

void eadd(int u,int v){
	edge[++ size].v = v;
	edge[size].u = u;
	edge[size].nt = first[u];
	first[u] = size;
}

void tarjan(int u){
	dfn[u] = low[u] = ++tot;
	vis[u] = 1;
	s[tp++] = u;
	
	for(int i = first[u];i;i = edge[i].nt){
		int v = edge[i].v;
		if(!dfn[v]){
			tarjan(v);
			low[u] = min(low[v],low[u]);
		}else if(vis[v]) low[u] = min(low[u],dfn[v]);
	}
	
	if(low[u] == dfn[u]){
		int x;++cnt;
		do{
			x = s[tp - 1];
			--tp;
			father[x] = cnt;
			a[cnt] ++;
			vis[x] =0;
		}while(x != u);
	}
}

int main(){
	read(n);read(m);
	for(int i = 1;i <= m; ++i){
		int u,v;
		read(u);read(v);
		eadd(u,v);
	}
	
	for(int i = 1;i <= n; ++ i){
		if(!dfn[i]) tarjan(i);
	}
	
	for(int i = 1;i <= m; ++ i){
		int u = edge[i].u;
		int v = edge[i].v;
		if(father[u] != father[v]){
			d[father[u]]++;
		}
	}
	
	int ans =  0;
	int num = 0;
	for(int i = 1;i <= cnt;++i){
		if(!d[i]){
			ans = a[i];
			num ++;
		}
		if(num >= 2) {
			puts("0");
			return 0;
		}
	}
	put(ans);
	return 0;
}

割点

#include<bits/stdc++.h>
#define M 200002
#define N 20002
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define F(i,l,r) for(int i=l;i<=r;++i)

using namespace std;

static int n,m,dfn[N],size=0,low[N],cnt=0,tot=0,f[N];
bool vis[N]={0};
struct E
{
	int v,nt;
};
static E e[M];
void eadd(int u,int v)
{
	e[++size].v=v;
	e[size].nt=f[u];
	f[u]=size;
}

void readdata()
{
	n=read(); m=read();
	F(i,1,m)
	{
		int x=read(),y=read();
		eadd(x,y);
		eadd(y,x);
	}
}

void tarjan(int u,int fa)
{
	dfn[u]=low[u]=++cnt;
	int cd=0;
	for(int i=f[u];i;i=e[i].nt)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			if(u==fa) ++cd;
			tarjan(v,u);
			low[u]=Min(low[u],low[v]);	
			if((u==fa&&cd>=2&&!vis[fa])||(u!=fa&&dfn[u]<=low[v]&&!vis[u])) ++tot,vis[u]=1;
		}
		else if(v!=fa)	low[u]=Min(low[u],dfn[v]);
	}
//	if((u==fa&&cd>=2&&!vis[fa])||(u!=fa&&dfn[fa]<=low[u]&&!vis[u])) ++tot,vis[fa]=1;
}

void work()
{
	F(i,1,n)
	{
		if(!dfn[i]) tarjan(i,i);
	}
	put(tot);
	putchar('\n');
	F(i,1,n) if(vis[i]) put(i),putchar(' ');
}

int main()
{
//	init();
	readdata();
	work();
	return 0;
}

看书吧

Topsort

这个就算了吧,栈和队列都可以

最小生成树

kruskal


int main(){
	read(n);read(m);
	for(int i =1;i <= n; ++ i) father[i] = i;
	for(int i = 1;i <= m;++i){
		int u,v,w;
		read(u);read(v);read(w);
		eadd(u,v,w);
	}
	sort(edge + 1,edge + m  + 1);
	for(int i  =1;i <= m; ++ i){
		int u = edge[i].u;
		int v = edge[i].v;
		int w = edge[i].w;
		
		if(find(u) != find(v)){
			merge(u,v);
			++cnt;
			ans += (long long)w;
		}
		if(cnt == n - 1) break; 
	}
	printf("%lld",ans);
	return 0;
 }

prim


void work(){
	memset(dis,0x3f3f3f3f,sizeof(dis));
	
	dis[1] = 0;
	
	priority_queue<Node>q;
	q.push(Node(1,0));
	
	while(!q.empty()){
		Node e = q.top();
		q.pop();
		int u = e.v,w = e.w;
		
		if(vis[u]) continue;
		
		vis[u] = 1;
		++cnt;
		ans += (long long)w;
		if(cnt == n) break;
		
		for(int i = first[u];i;i = edge[i].nt){
			int v = edge[i].v;
			int w = edge[i].w;
			if((!vis[v]) && w < dis[v]){
				dis[v] = w;
				q.push(Node(v,w));
			}
			
		}
	}
	
	printf("%lld",ans);
}

n^2

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

int n,m,d[5005],p,t,s,h[5005];
struct ee
{
	int u,v,w,nt;
}e[400005];

void eadd(int x,int y,int z)
{
	e[++s].u=x;
	e[s].v=y;
	e[s].w=z;
	e[s].nt=h[x];
	h[x]=s;
}

void init()
{
	freopen("1.txt","r",stdin);
}

void readdata()
{
	scanf("%d%d",&n,&m);
	
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		eadd(x,y,z);
		eadd(y,x,z);
	}
}

void work()
{
	memset(d,0x3f,sizeof(d));
	d[1]=0;
	for(int i=h[1];i;i=e[i].nt) 
	{
		if(e[i].w<d[e[i].v])//重边 
		d[e[i].v]=e[i].w;
	}
	for(int i=1;i<n;i++)
	{
		p=2000000000;
		int x;
		for(int j=2;j<=n;j++)
		{
			if(d[j]&&d[j]<p)
			{
				p=d[j];
				x=j;
			}
		}
		t+=p;
		d[x]=0;
		for(int j=h[x];j;j=e[j].nt) if(d[e[j].v]>e[j].w) d[e[j].v]=e[j].w;
	}
	printf("%d",t);
}

int main()
{
//	init();
	readdata();
	work();
	return 0;
}

瓶颈生成树,最小瓶颈路,记住结论

严格次小生成树

/**********************
User:Mandy
Language:c++
Problem:luogu
Algorithm: 
**********************/

#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 5;
const int maxm = 3e5 + 5;

int n,m,size,first[maxn],fa[maxn],dep[maxn];
int father[maxn][20],val[maxn][20][2];
bool vis[maxm];
long long ans,sum;
int val1,val2;

struct Edge{int v,u,w;}E[maxm];
struct EE{int v,nt,w;}edge[maxm << 1];

bool cmp(const Edge &a,const Edge &b) {return a.w < b.w;}
int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}
void eadd(int u,int v,int w) {edge[++size].v=v;edge[size].w=w;edge[size].nt=first[u];first[u]=size;}

void readdata(){
	read(n);read(m);
	for(int i = 1;i <= m; ++ i){read(E[i].u);read(E[i].v);read(E[i].w);}
}

void dfs(int u,int f){
	father[u][0] = f;
	for(int i = 1;i <= 19; ++ i) {
		int ff = father[u][i-1],j = i-1;father[u][i] = father[ff][j];
		if(val[u][j][0] == val[ff][j][0]){
			val[u][i][0] = val[u][j][0];
			val[u][i][1] = max(val[u][j][1],val[ff][j][1]);
		} else {
			if(val[u][j][0] > val[ff][j][0]){
				val[u][i][0] = val[u][j][0];
				val[u][i][1] = max(val[ff][j][0],val[u][j][1]);
			} else {
				val[u][i][0] = val[ff][j][0];
				val[u][i][1] = max(val[u][j][0],val[ff][j][1]);	
			}
		}
	}
	for(int i = first[u];i;i = edge[i].nt){
		int v = edge[i].v;if(v == f) continue;
		val[v][0][0] = edge[i].w;dep[v] = dep[u]+1;dfs(v,u);
	}
}

void get_max(int u,int v){
	val1 = -2e9,val2 = -2e9;
	if(dep[u]<dep[v]) swap(u,v);
	for(int i = 19;i >= 0; -- i){
		if(dep[father[u][i]]>=dep[v]){
			if(val[u][i][0]>=val1){//大于等于,不能让次大值等于最大值 
				val1 = val[u][i][0];
				val2 = max(val2,val[u][i][1]);
			}else val2 = max(val2,val[u][i][0]);
			u = father[u][i];
		}
	}
	if(u == v) return;//!!!!!!
	for(int i = 19;i >= 0; -- i){
		if(father[u][i] != father[v][i]){
			if(val[u][i][0]>=val1){
				val1 = val[u][i][0];
				val2 = max(val2,val[u][i][1]);
			}else val2 = max(val2,val[u][i][0]);
			u = father[u][i];
			if(val[v][i][0]>=val1){
				val1 = val[v][i][0];
				val2 = max(val2,val[v][i][1]);
			}else val2 = max(val2,val[v][i][0]);
			v = father[v][i];	
		}
	}
	if(val[u][0][0]>=val1){
		val1 = val[u][0][0];
		val2 = max(val2,val[u][0][1]);
	}else val2 = max(val2,val[u][0][0]);
	u = father[u][0];
	if(val[v][0][0]>=val1){
		val1 = val[v][0][0];
		val2 = max(val2,val[v][0][1]);
	}else val2 = max(val2,val[v][0][0]);
	v = father[v][0];	
}

void work(){
	sort(E+1,E+m+1,cmp);
	for(int i = 1;i <= n; ++ i) fa[i] = i;
	for(int i = 1;i <= m; ++ i) {
		int u = E[i].u,v = E[i].v;
		int fx = find(u),fy = find(v);
		if(fx != fy){sum += E[i].w;vis[i]=1;fa[fx] = fy;eadd(u,v,E[i].w);eadd(v,u,E[i].w);}
	}
	dep[1] = 1;dfs(1,0);ans = 1e17;
	for(int i = 1;i <= m; ++ i){
		if(vis[i]) continue;
		get_max(E[i].u,E[i].v);
		if(val1 == E[i].w) ans = min(ans,sum-val2+E[i].w);
		else ans = min(ans,sum-val1+E[i].w);//当最大值等于要加的边,就用次大值 
	}
	put(ans);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

最短路径生成树

/**********************
User:Mandy
Language:c++
Problem:luogu
Algorithm: 
**********************/

#include<bits/stdc++.h>

using namespace std;

const int maxn = 1005;
const int maxm = 1000005;
const long long mod = (1ll<<31)-1;

int n,m,dis[maxn];
int size,first[maxn];
long long cnt[maxn];
bool vis[maxn];

struct Edge{
	int v,nt,w;
}edge[maxm];

struct Node{
	int u,dis;
	Node(int _u=0,int _dis=0):u(_u),dis(_dis){}
	bool operator <(const Node &a)const{return dis>a.dis;}
}d[maxn];

template<class T>inline void read(T &x){
	x = 0;char ch = getchar();bool flag = 0;
	while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag) x = -x;
} 

template<class T>void putch(const T x){
	if(x > 9) putch(x / 10);
	putchar(x % 10 | 48);
}

template<class T>void put(const T x){
	if(x < 0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("349.in","r",stdin);
	freopen("349.out","w",stdout);
} 

void eadd(int u,int v,int w){
	edge[++size].v = v;
	edge[size].w = w;
	edge[size].nt = first[u];
	first[u] = size;
}

void readdata(){
	read(n);read(m);
	for(int i = 1;i <= m; ++ i){
		int u,v,w;read(u);read(v);read(w);
		eadd(u,v,w);eadd(v,u,w);
	}
}

void dijkstra(){
	priority_queue<Node>q;
	memset(dis,0x3f3f3f3f,sizeof(dis));
	q.push(Node(1,0));dis[1] = 0;
	while(!q.empty()){
		int u = q.top().u;q.pop();
		if(vis[u])continue;vis[u]=1;
		for(int i = first[u];i;i = edge[i].nt){
			int v = edge[i].v;
			if(dis[u]+edge[i].w < dis[v]){
				dis[v] = dis[u] + edge[i].w;
				q.push(Node(v,dis[v]));
			}
		}
	}
}

bool cmp(const Node &x,const Node &y){
	return x.dis < y.dis;
}

void work(){
	dijkstra();
	for(int i = 1;i <= n; ++ i) d[i].u=i,d[i].dis=dis[i];
	sort(d+1,d+n+1,cmp);
	for(int i = 1;i <= n; ++ i){
		int u = d[i].u;//取出节点 
		for(int j = first[u];j;j = edge[j].nt){//遍历边 
			int v = edge[j].v;
			if(dis[v] == dis[u]+edge[j].w) cnt[v]++;
		}
	} 
	long long ans = 1;cnt[1]=1;//初始化 
	for(int i = 1;i <= n; ++ i) ans = ans*cnt[i]%mod;
	put(ans);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

最小度限制生成树

/*********************
User:Mandy
Language:c++
Problem:luogu
Algorithm: 
*********************/
//这道题,明显矩阵好做一点 
//断边的时候链表不好断 
#include<bits/stdc++.h>

using namespace std;

const int maxn = 35;

int n,cnt,s,md,size,tot,ans = 0,fa[maxn],tmp[maxn],t;
int len[maxn],father[maxn];
char name[maxn<<1][maxn]; 
//bool vis[maxn];
int edge2[maxn][maxn]; 

struct Edge{
	int u,v,w;
}edge[maxn*maxn],edge1[maxn]; 

void eadd(int u,int v,int w){
	edge[++size].u = u;edge[size].v = v;edge[size].w = w;
}

bool same(int x,int y){
	if(len[x] != len[y]) return 0;
	for(int i = 0;i < len[x]; ++ i)
		if(name[x][i]!=name[y][i]) return 0;
	return 1;//return 1 
}

void readdata(){
	read(n);
	for(int i = 1;i <= n; ++ i){
		int u=-1,v=-1,w=-1;memset(name[cnt],0,sizeof(name[cnt]));
		scanf("%s",name[cnt]);len[cnt] = strlen(name[cnt]);
		for(int j = 0;j < cnt; ++ j) if(same(cnt,j)) {u = j;break;}
		if(u==-1) u = cnt,cnt++;memset(name[cnt],0,sizeof(name[cnt]));
		scanf("%s",name[cnt]);len[cnt] = strlen(name[cnt]);
		for(int j = 0;j < cnt; ++ j) if(same(cnt,j)) {v = j;break;}
		if(v==-1) v = cnt,cnt++;	
		read(w);eadd(u,v,w);
	}
	memset(name[cnt],0,sizeof(name[cnt]));//清空 
	name[cnt][0] = 'P';	name[cnt][1] = 'a';
	name[cnt][2] = 'r';	name[cnt][3] = 'k';
	len[cnt] = 4;//更改len值 
	for(int i = 0;i < cnt; ++ i)
		if(same(cnt,i)){s = i;break;}
	read(md);
}

void init(){
	memset(name,0,sizeof(name));memset(fa,-1,sizeof(fa));//注意初始化 
	memset(father,0,sizeof(father));memset(tmp,0,sizeof(tmp));
	memset(edge,0,sizeof(edge));memset(edge1,0,sizeof(edge1));
	memset(edge2,0,sizeof(edge2));memset(len,0,sizeof(len));
	n = 0,cnt = 0 ,s = 0,md = 0,size = 0,tot = 0,ans = 0;
}

bool cmp(const Edge &a,const Edge &b){
	return a.w < b.w;
}

int find(int x){
	return father[x] == x ? x : father[x] = find(father[x]);
}

void dfs(int u,int f){
	if(fa[u] == f) return;//剪枝 
	fa[u] = f;
	for(int i = 0;i < cnt;++ i){
		if(i == f || i == u || !edge2[u][i]) continue;
		tmp[i] = edge2[u][i];dfs(i,u);
		//记录v与父亲的边的边权 
	}
}

void work(){
	sort(edge+1,edge+n+1,cmp);size = 0;
	//与s相连的放在edge1里,用结构体,保证小的在前
	//防止后面会更新连着S的边,而连着S的这条边不是最大
	for(int i = 0;i <= cnt; ++ i) father[i] = i;
	for(int i = 1;i <= n; ++ i){
		int u = edge[i].u,v = edge[i].v,w = edge[i].w;
		if(u == s || v == s){edge1[++tot] = edge[i];continue;}
		int fx = find(u),fy = find(v);
		if(fx != fy) ans += edge[i].w,father[fx] = fy,edge2[u][v] = edge2[v][u] = w;
	}
	
	//如果连通块的数量大于md,则无解
	//不过这题好像没有说无解怎么办 
	//那就先默认有解 
	while(md){
		int num = 0,id=0,idu=0;
		for(int i = 1;i <= tot; ++ i){
			int u = edge1[i].u,v = edge1[i].v;
			int fx = find(u),fy = find(v);
			int x = u == s ? v:u;
			if(fx != fy) {
				father[fy] = fx;tmp[x] = edge1[i].w;
				dfs(x,s);md--;ans+=tmp[x];
			} else {//找最大的可断边 
				int f = x,cur = 0,xx;
				while(f!=s){
					if(cur < tmp[f]){cur = max(cur,tmp[f]);xx = f;}//记录要断的边  
					f = fa[f];
				}
				if(cur-edge1[i].w>num) {num = cur-edge1[i].w;id = i;idu = xx;}
				//注意edge1 
				//idu是要断的边,id是要连的边(点) 
			}
		}
		if(fa[idu] == s) md++;//如果断点与起点相连 
		if(num > 0 && md){
			md--;
			ans -= num;
			edge2[idu][fa[idu]]=0;
			edge2[fa[idu]][idu] = 0;//断边 
			int xx = edge1[id].u == s ? edge1[id].v:edge1[id].u;
			dfs(xx,s);//重新定义父子 
			tmp[xx] = edge1[id].w;//注意tmp[xx] 
		} else break;
	}
	
	printf("Total miles driven: %d\n",ans);//这个换行有毒 
}

int main(){
//	file();
	read(t);
//	t = 1; 
	while(t--){
		init();
		readdata();
		work();
		if(t) puts(""); //这个换行有毒 
	}
	return 0;
}

最小生成树计数

#include<bits/stdc++.h>

using namespace std;

const int maxn = 105;
const int maxm = 1005;
const int mod = 31011;

int n,m,size,tot;
int father[maxn];
int cnt,ans,sum;
int son[maxn];

struct Node{
	int l,r,num;
}a[maxm];

struct Edge{
	int u,v,w;
}edge[maxm];

void eadd(int u,int v,int w){
	edge[++size].v = v;
	edge[size].u = u;
	edge[size].w = w;
}

bool cmp(const Edge &a,const Edge &b){
	return a.w < b.w;
}

void readdata(){
	read(n);read(m);
	for(int i = 1;i <= m; ++ i){
		int u,v,w;
		read(u);read(v);read(w);
		eadd(u,v,w);
	}
	sort(edge + 1,edge + m + 1,cmp);
}

int find1(int x){//压缩路径 
	return father[x] == x ? x : father[x] = find1(father[x]);
}

int find2(int x){//不压缩路径 
	return father[x] == x ? x : find2(father[x]);
}

void merge(int x,int y){
	father[find1(x)] = find1(y);
}

void merge1(int x,int y){
	if(son[x] > son[y]){
		father[y] = x;
		son[x] += son[y];
		son[y] = son[x];
	} else {
		father[x] = y;
		son[y] += son[x];
		son[x] = son[y];
	}
}//按秩合并 

void dfs(int id,int pos,int num){
	//id - 边的长度的编号
	//pos - 边的编号
	//num - 这条边在最小生成树中的个数 
	if(pos == a[id].r + 1){//到边了 
		if(num == a[id].num) sum++;
		return;//如果不等于在最小生成树中的个数,说明方案错了 
	}
	int u = find2(edge[pos].u);
	int v = find2(edge[pos].v);//不能压缩路径 
	if(u != v){
		father[u] = v;
		dfs(id,pos+1,num +1);//选这条边 
		father[u] = u;
	}
	dfs(id,pos + 1,num);//不选这条边 
}

void work(){
	edge[0].w = -10000;
	edge[m+1].w = -10000;
	for(int i = 1;i <= n; ++ i) father[i] = i;
	for(int i = 1;i <= m; ++ i){
		int u = find1(edge[i].u);
		int v = find1(edge[i].v);
		if(edge[i-1].w != edge[i].w){
			a[cnt].r = i-1;
			a[++cnt].l = i;//a数组中存下相同长度的边的左右边界 
		}
		if(u != v){
			a[cnt].num++;//存下这个长度的边在最小生成树中的个数 
			merge(u,v);
			++tot;
		}
	}
	a[cnt].r = m;//给最后一个加上右边界 
	if(tot != n-1) {
		puts("0");
		return;
	}
	for(int i = 1;i <= n; ++ i) father[i] = i;
	for(int i = 1;i <= n; ++ i) son[i] = 1;
	ans = 1;//初始化 
	for(int i = 1;i <= cnt; ++ i){
		sum = 0;
		dfs(i,a[i].l,0);
		ans = ans * sum % mod;
		for(int j = a[i].l;j <= a[i].r; ++ j){
			int fx = find2(edge[j].u);
			int fy = find2(edge[j].v);//按最小生成树连边 
			if(fx != fy) merge1(fx,fy);
		}
	}
	printf("%d",ans);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值