浅浅6.5w字小整理,算法入门入个坑,壬寅年就学到这了,癸卯年变得更强

高精度乘法

反向输入,123*2-246 C:642
设置一个进位t,正向处理每一 位,每- -次都是加 上然后把的个位塞进C,最后反相输入

#include<vector>
 

using namespace std;
vector<int> mul(vector<int> &A,int b)
{
	vector<int>C;
	
	int t=0;
	for(int i=0;i<A.size()||t;i++)
	{
		if(i<A.size())t+=A[i]*b;
		C.push_back(t%10);
		t/=10; 
	}
	return C;
}
int main()
{
	string a;
	int b;
	
	cin>>a>>b;
	vector<int>A,C;
	for(int i=a.size() -1;i>=0;i--)
	{
		A.push_back(a[i]-'0'); 
	}
	C=mul(A,b);
	for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
	return 0;
}

二维差分

解决的是对于一个矩形区间内加上- -个数字,左上角+C,右下角+C,其他都是-C
最后和起来的时候减去左上角
求一个独立区间的,是用右下角-- +左上角

#include<iostream>
using namespace std;
const int N=1010;
int n,m,q;
int a[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
	b[x1][y1]+=c;
	b[x1][y2+1]-=c;
	b[x2+1][y1]-=c;
	b[x2+1][y2+1]+=c;
}
int main()
{
   scanf("%d%d%d",&n,&m,&q);
   
   for(int i=1;i<=n;i++)
   {
   	for(int j=1;j<=m;j++)
   	scanf("%d",&a[i][j]);
	   }	
	
   for(int i=1;i<=n;i++)
   {
   	for(int j=1;j<=m;j++)
   	{
   		insert(i,j,i,j,a[i][j]);
	   }
   }
   while(q--)
   {
   	int x1,y1,x2,y2,c;
   	cin>>x1>>y1>>x2>>y2>>c;
   	insert(x1,y1,x2,y2,c);
   }
   for(int i=1;i<=n;i++)
   {
   	for(int j=1;j<=m;j++)
   	{
   		b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1]; 
   		
	   }
   }
   
   for(int i=1;i<=n;i++)
   {
   	for(int j=1;j<=m;j++)
   	printf("%d",b[i][j]);
   	puts(" ");
   }
   return 0;
   
}

双指针

解决前面后面某个单调序列里面的关系问题

往往原型是两重循环
变成一重循环+while(j<=i&&check(j,i))j++

举个例子:最长连续不重复子序列

#include<iostream>
  using namespace std;
  const int N=100010;
  int a[N],n;
  int s[N];
  int main()
  {
  	cin>>n;
  	for(int i=0;i<n;i++)
  	{
  		cin>>a[i];
	  }
  	
  	int res=0;
  	for(int i=0,j=0;i<n;i++)
  	{
  	  s[a[i]]++;
  	  while(s[a[i]]>1)
  	  {
  	  	s[a[j]]--;
  	  	j++;//j用来删掉重复的 
		}
	  res=max(res,i-j+1);
	  }
	  
	  cout<<res<<endl;
  	  return 0; 
   } 

位运算.

x&-x
n>>i&1

单调队列

53 543 2<5- 3+1 i-k+1>q[h h]
t=5 t=4 q[5] ql++t]=I;
用在在某一前一定范围内取这一范围内最大最小值, h往往是要用的值(最小or最大)

应用:滑动窗口

#include<iostream>
using namespace std;
const int N=100010;
int a[N],q[N];
int n,k;
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;i++)
	scanf("%d",&a[i]);
	
	int hh=0,tt=-1;
	for(int i=-1;i<n;i++)
	{
		//判断对头是否画出
		if(hh<=tt&&i-k+1>q[hh])hh++;
		while(hh<=tt&&a[q[tt]]>=a[i]) tt--;
		q[++tt]=i;
		if(i>=k-1) printf("%d",a[q[hh]]);
		
	}
	puts(" ");
}

Kmp

类似于双指针,i=2,j=0 预处理那个短的

While p[i]!=p[i+1]j=ne[j];
(if(i==pl[+1])j++
next[i]=j;

#include<iostream>
using namespace std;
const int N=10010,M=100010;

int n,m;
char p[N],s[N];
int ne[N];

int main()
{
	cin>>n>>p+1>>m>>s+1;
        
	//求next
	 for(int i=2,j=0;i<=n;i++)
	 {
	 	while(j&&p[i]!=p[j+1])j=ne[j];
	 	if(p[i]==p[j+1]) j++;
	 	ne[i]=j;	  } 
	
	//kmp匹配过程 
	for(int i=1,j=0;i<=m;i++)
	{
	while(j&&s[i]!=p[j+1]) j=ne[j];
	if(s[i]==p[j+1]) j++;
	if(j==n)
	{
		printf("%d",i-n+1);
		j=ne[j];
		}	
	}
	return 0;
}

离散化.

  • 无序: get (x) if(S.count(x)= =0)S[x]=++n;unordered. map
  • 有序:ally.push_ back()
    sort(ally .begin(,ally .end())
    ally .erase (unique(ally .begin(), ally .end() ),ally .end())
    x=find(x);find: 用二分

高精度除法

4567/12
反向输入7654
设置一个r 反向输出r*10+ a[i] temp正向存入r/b 然后反向去前导零,再反向输出(我觉
得可以之间返回了)
整数二分找到第一个等于这个数的位置q[mid]> =x r= mid;
找到最后一个
q[mid]<=x |=mid

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;
vector<int> div(vector<int> &A,int b,int &r)
{
	vector<int>C;
	r=0;
	for(int i=A.size() -1;i>=0;i--)
	{
		r=r*10+A[i];
		C.push_back(r/b);
		r%=b; 
	 } 
	 reverse(C.begin() ,C.end() );
	 while(C.size()>1&&C.back() ==0)C.pop_back(); 
	return C;
}
int main()
{
	string a;
	int b;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
	
	cin>>a>>b;
	vector<int>A,C;
	for(int i=a.size() -1;i>=0;i--)
	{
		A.push_back(a[i]-'0'); 
	}
	int r;
	C=div(A,b,r);
	for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
	cout<<endl;
	cout<<r<<endl;
	return 0;
}

高精度加法

字符串读入并且把每一位转化成数字存入数组 12345=>54321
从第一位开始增加,设置一个进位t,c里面存t%10
最后如果t还有的话要向答案里push1
最后反向输出C

#include<iostream>
#include<vector>

using namespace std;
const int N=1e6+10;
vector<int> add(vector<int> &A,vector<int> &B)
{
	vector<int> C;
	int t=0; //进位 
	for(int i=0;i<A.size() ||i<B.size() ;i++)
	{
		if(i<A.size())t+=A[i];
		if(i<B.size())t+=B[i];
		C.push_back(t%10);
		t/=10; 
	}
	if(t) C.push_back(1);
	return C; 
}
int main()
{
	string a,b;
	vector<int> A,B,C;
	
	cin>>a>>b;
	for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
	 
	 C=add(A,B);
	
	for(int i=C.size()-1;i>=0;i--)
	{
	//	printf("%d",C[i]);
	    cout<<C[i];
	}
	return 0;
}

高精度减法

设置一个t,每一次t=减数-t,再用t去减被减数,如果t<0,t=1

#include<iostream>
#include<vector>

using namespace std;
const int N=1e6+10;
vector<int> add(vector<int> &A,vector<int> &B)
{
	vector<int> C;
	int t=0; //进位 
	for(int i=0;i<A.size() ||i<B.size() ;i++)
	{
		if(i<A.size())t+=A[i];
		if(i<B.size())t+=B[i];
		C.push_back(t%10);
		t/=10; 
	}
	if(t) C.push_back(1);
	return C; 
}
int main()
{
	string a,b;
	vector<int> A,B,C;
	
	cin>>a>>b;
	for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
	 
	 C=add(A,B);
	
	for(int i=C.size()-1;i>=0;i--)
	{
	//	printf("%d",C[i]);
	    cout<<C[i];
	}
	return 0;
}

Trle树

int p=0;
for(int i=0;str[i];i++)
int u=str[i]-a';
if(!son[p][u])son[p][u]=idx++;
p=son[p][u];

int query(char str[])
{
	int p=0;
	for(int i=0;str[i];i++)
	{
		int u=str[i]-'a';
		if(!son[p][u])return 0;
		p=son[p][u];
	}
	return cnt[p];
}

哈希函数

  • 拉链法:
e[idx]=x;ne[idx]=h[k]
h[k]=idx++;
If e[x]==x return true;
  • 开放寻址法
while(h[k]!=null&h[k]! =x)
{k+ +;i(k= =N)k=0;}
return k;
字符串:选择P=131 13331
用一个P进制for (int i=1;i<=n;i++) 

字符串哈希

用p进制(131,13331)

for(int i=1;i<=n;i++)
{
 p[i]=p[i-1]*P;
 h[i]=h[i-1]*P+str[i]
 }
get :return h[r]-h[l-1]*p[r-l+1]

堆排序(小顶堆)

void up
while(u/2&&h[u/2]>h[u])
{  heap_swap(u/2,u)
  u/=2;}

void down
int t=u;
if(u*2<=size&&h[u*2]<h[t])t=u*2;
if(u*2+1<=size&&h[u*2+1]<h[t])t=u*2+1;
if(u!=t){heap_swap(u,t),down(t);}

八皇后

dfs参数是行数,则函数内部,对于列进行深搜,每选择一个列,满足竖斜斜不矛盾

void dfs(int u)
{
	if(u==n)
	{
		for(int i=0;i<n;i++)
		{
			puts(g[i]);
		}
		puts("");
		return ;
	}
	for(int i=0;i<n;i++)
	{
		if(!col[i]&&!dg[u+i]&&!udg[n-u+i])
		{
			g[u][i]='Q';
			col[i]=dg[u+i]=udg[n-u+i]=true;
			dfs(u+1);
			col[i]=dg[u+i]=udg[n-u+i]=false;
			g[u][i]='.';
		}
	}
	
 } 

dfs参数是坐标和皇后的个数,就是一个一个的枚举放不放
y不断增加,直到n,然后清零,x++,
不放皇后(x,y+1,s)
如果满足条件 :横竖两斜 dfs(x,y+1,s+1)

void dfs(int x,int y,int s)
{
	if(y==n)y=0,x++;
	if(x==n)
	{
		if(s==n)
		{
			for(int i=0;i<n;i++)
			puts(g[i]);
			puts("");
		}
		return ;
	 } 
	 //不放皇后
	 dfs(x,y+1,s);
	 //放皇后
	 if(!row[x]&&!col[y]&&!dg[x+y]&&!udg[x-y+n])
	 {
	 	g[x][y]='Q';
	 	row[x]=col[y]=dg[x+y]=udg[x-y+n]=true;
	 	dfs(x,y+1,s+1);
	 	row[x]=col[y]=dg[x+y]=udg[x-y+n]=0;
	 	g[x][y]='.';
	  } 
	
 } 

不能忘记 memset(h,-1,sizeof h)
树的重心
深搜到叶结点,然后利用返回值跟新答案
int s=dfs(j)
res=max(res,s)
sum+=s;

res=max(res,n-sum)
ans=min(ans,res)

bfs

用队列去存储数据
先要设置h,t,q[0],d
while循环内,每次取出队头,进行操作,存入队列

寻找路径

Prev[x][y]=t
x=目标,y=目标
while(x||y)
{ cout<<x<<" "<<y;
PII t=Prev[x][y];
x=t.first,y=y.second}

bellman

用于存在负权求单源最短路(当限制了边数,就用这个)
思想:用边存两个端点和长度的信息
把所有的距离最大化,dist[1]=0
循环可以拓展的次数,每一次要把上一次的dist备份给backup,循环所有边数,到to顶点的距离被更新(防止被一次又一次更新)
如果dist[n]>0x3f3f3f3f/2,相当于无法到达

int bellman_ford()
{
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
	for(int i=1;i<=k;i++)
	{
		memcpy(backup,dist,sizeof dist);
		for(int j=0;j<m;j++)
		{
			int a=edges[j].a,b=edges[j].b,w=edges[j].w;
			dist[b]=min(dist[b],backup[a]+w);
		}
	}
	
	if(dist[n]>0x3f3f3f3f/2)return -1;
	return dist[n];
}

朴素版dijkstra

稠密图,所有边权为正,邻接矩阵
每次寻找不在S中距离最近的点作为下一个拓展的中间点,用它去进行更新

memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{if(!st[j]&&(t==1||dist[t]>dist[j]);t=j;}
st[t]=true;
for(int i=1;i<=n;i++)
{dist[j]=min(dist[j],dist[t]+g[t][j]);}
if(dist[n]==0x3f3f3f3f)return -1;
	return dist[n];

堆优化版本dijkstra

优先队列当堆 存着pair <到源距离,哪个点>
只需要一个循环,每一次去拉伸,存入,由于是优先队列,所以会自然按照距离排序,如果这个点已经被收入了就跳过

memset(dist,0x3f,sizeof dist);
dist[1]=0;
priority_queue<PII,vector<PII> >heap;
heap[0]={0,1};
while(heap.size())
{PII t=heap.top();
heap.pop();
int distance =t.firse,ver=t.second;
if(st[ver])continue;
st[ver]=1;
for(int i=h[[ver];i!=-1;i=ne[i])
{ int j=e[i];
   if(dist[j]>distance+w[i])
   { dist[j]=distance+w[i];
   heap.push({dist[j],j};
   }
 }

SPFA

可以用于有负权的最小路
使用队列 每一次是自己相连的几个点去拓展,先拓展,如果这个被拓展且不在队列就加入队列,所以可以每个点拓展无数次,达到各种目的

int spfa()
{
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
	
	queue<int>q;
	q.push(1);
	st[1]=1;
	
	while(q.size())
	{
		int t=q.front();
		q.pop();
		st[t]=false;
		for(int i=h[t];i!=-1;i=ne[i])
		{
			int j=e[i];
			if(dist[j]>dist[t]+w[i])
			{
				dist[j]=dist[t]+w[i];
				if(!st[j])
			   {
			   	 q.push(j);
				 st[j]=true;  
			   }
			}
		}
	}
	if(dist[n]==0x3f3f3f3f) return -1;
	return dist[n];	  
}

kruscal

用于稀疏图·
用边存两个端点,和距离
对于距离进行排序,看最小边的两个端点是不是属于一个树,不是的话就把他们合并,利用并查集,如果最后连接的边数<n-1,证明无法形成树

#include<iostream>
#include<algorithm>

using namespace std;
const int N=100010;
int n,m;
int p[N];

struct Edge
{
	int a,b,w;
	bool operator<(const Edge &W)const
	{
		return w<W.w;
	}
 } edges[N];
 int find(int x)
 {
 	if(x!=p[x])p[x]=find(p[x]);
 	return p[x];
 }
 int main()
 {
 	scanf("%d%d",&n,&m);
 	for(int i=0;i<m;i++)
 	{
 		int a,b,w;
 		scanf("%d%d%d",&a,&b,&w);
		 edges[i]=(Edge){a,b,w}; 
	 }
	 sort(edges,edges+m);
	 for(int i=1;i<=n;i++)
	 p[i]=i;
	 
	 int res=0,cnt=0;
	 for(int i=0;i<m;i++)
	 {
	 	int a=edges[i].a,b=edges[i].b,w=edges[i].w;
	 	a=find(a),b=find(b);
	 	if(a!=b)
	 	{
	 		p[a]=b;
	 		res+=w;
	 		cnt++;
		 }
	 }
	 
	 if(cnt<n-1)puts("impossible");
	 else printf("%d\n",res);
	 return 0;
}

prim

类似于dijkstra,用于存储稠密图

int prim()
{
	memset(dist,0x3f,sizeof dist);
	
	int res=0;
	for(int i=0;i<n;i++)
	{
		int t=-1;
		for(int j=1;j<=n;j++)
		if(!st[j]&&(t==-1||dist[t]>dist[j]))
		  t=j;
		  
		if(i&&dist[t]==INF) return INF;
		if(i) res+=dist[t];
		for(int j=1;j<=n;j++)
		dist[j]=min(dist[j],g[t][j]);
		
		
		st[t]=1;
	}
	return res;
}

染色法二分图

用于判断给出的一组点一张图能否进行二分,循环所有的点,对于没有被染色的点进行深搜
深搜是先把它涂色,对于它的连的边里没有染过色的,进行深搜,如果它的子结点和他是一个颜色,或者他子节点的子结点和他是一个颜色(存在奇数环,等的就是绕回自己的时候,else if就是给最后一个准备的,然后就会不断返回负值)

for(int i=1;i<=n;i++)
if(!color[i])
{if(!dfs(i,1)
break;
}

bool dfs(int u ,int c)
{
	color[u]=c;
	for(int i=h[u];i!=1;i=ne[i])
	{
		int j=e[i];
		if(!color[j])
		{
			if(!dfs(j,3-c)) return false;
		}
		else if(color[j]==c)return false;
	}
}

匈牙利算法

一一匹配
对于左边所有点循环,每一次把st设为false
进行寻找

寻找过程,循环可以的配对对象,如果这个st是false,再去看是否配对了,或者原本对象可以再找一个,就把他配对

bool find(int x)
{
	for(int i=h[x];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(!st[j])//st是为了让前夫哥寻找的时候pass掉找自己其他的 
		{
			st[j]=true;
			if(match[j]==0||find(match[j]))
			{
				match[j]=x;
				return true;
			}
		}
	}
	return false;
}

约数个数

一系列数字乘积的约数个数,每个约数选择的可能性相乘
用无序表存储个数和对应的数字

int main()
{
	int n;
	cin>>n;
	unordered_map<int,int>primes;
	unordered_map<int,int>::iterator prime; 
	while(n--)
	{
		int x;
		cin>>x;
		
		for(int i=2;i<=x/i;i++)
		while(x%i==0)
		{
			x/=i;
			primes[i]++;
		}
		if(x>1)prime[x]++;
	}
	ll res=1;
	for(prime=primes.begin();prime<=primes.end();prime++)
	{
		res=res*(prime.second+1)%mod;
	}
	cout<<res<<endl;
	
 } 

质数

普通筛法

for(int i=2;i<=n/i;i++)
{
  if(n%i==0)
  {
     int s=0;
     while(n%i==0)
     {
        n/=i;
        s++;
       }
       cout<<i<<s;
    }
    if(n>1)cout<<n<<1;
   }
 

埃式筛法
找到一个质数,然后接下去每+这个质数,肯定不是质数,标记了存在

for(int i=2;i<=n/i;i++)
{if(!st[i])
  { primes[cnt++]=i;
  for(int j=i*i;j<=n;j+=i)
  st[j]=1;
  }
 }

线性筛法
根据最小质因数去筛

for(int i=2;i<=n;i++)
{ if(!st[i])
   { primes[cnt++]=i;
     for(int j=0;primes[j]<=n/i;j++)
     {
     st[primes[j]*i]=1;
     if(i%primes[j]==0)break;
     }
    }
 }

欧拉函数

1-N中与N互质的数的个数成为欧拉函数
N=p1^a1 *p2 ^a2 pm ^am
函数=N
(p1-1/p1)(p2-1/p2)(pm-1/pm)

		int a;
		cin >> a;
		int res = a;
		for (int i = 2; i <= a / i; i++)
			if (a % i == 0)
			{
				res = res / i * (i - 1);//为了保证是个整数,从:res=res*(1-1/i)而来
				while (a % i == 0)a /= i;
		    }
		if (a > 1) res = res / a * (a - 1);
		cout << res << endl;

欧拉定理

a与n互质,则a的n的欧拉函数次幂模上n是1,n是质数,欧拉函数是n-1

快速幂

a的k次方mod p

int qmi(int a,int k,int p)
{int res=1;
 while(k)
 {if(k&1)res*=a%p;
 k>>=1;
 a=(ll)a*a%p;
 }
return res;
}

欧几里得以及其扩展

int gcd(int a,int b)
return b?gcd(b,a%b):a;

扩展欧几里得

裴蜀定理:有任意一对正整数a,b,那么一定存在非零整数x,y,使得ax+by=gcd(a,b)

int exgcd(int a,int b,int &x,int &y)
{
if(!b){x=1,y=0;return a;}
int d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}

高斯消元

解的个数:无解 有一个n==0
一解 正好
无数解 少一个条件

计算顺序 循环每一列
找到绝对值最大的那一行,把这一 行换到最顶端
将这一行第一个数字变成1
通过减法,把下面每一行的这一列变成0

int guess()
{
	int c,r;
	for(c=0,r=0;c<n;c++)
	{
		int t=r;
		for(int i=r;i<n;i++)
		{
			if(fab(a[i][c])>fabs(a[t][c]))
			t=i;
		}
		if(fabs(a[t][c])<eps)continue;
		
		for(int i=c;i<=n;i++)swap(a[t][i],a[r][c]);
		for(int i=n;i>=c;i--)a[r][i]/=a[r][c];
		for(int i=r+1;i<n;i++)
		  if(fabs(a[i][c])>eps)
		   for(int j=n;j>=c;j--)
		   a[i][j]-=a[i][c]*a[r][j];
	    r++;
	}
		if (r < n)
	{
			for (int i = r; i < n; i++)
			{
				if (fabs(a[i][n] > eps))
					return 2;//无解
			 }
			return 1;//五穷解
	}

	for (int i = n - 1; i >= 0; i--)
	{
		for (int j = i + 1; j < n; j++)
		  a[i][n] -= a[i][j] * a[j][n];
	}
		return 0;
 } 

组合数

10w组 1-2000
用递推公式

for(int i=0;i<N;i++)
for(int j=0;j<=i;j++)
{ if(!j)c[i][j]=1;
  else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
  }

1w组,1-1e5
预处理出来阶乘
根据欧拉定理 a^p-2 * p(modp)为1,所以p的倒数就是a^p-2

fact[0]=infact[0]=1;
for(int i=1;i<N;i++)
{ fact[i]=fact[i-1]*i%mod;
   infact[i]=infact[i-1]*qmi(i,mod-2,mod)%mod;
   }
 最后输出 fact[a]*infact[b]*infact[a-b]

20组,1-10^18
卢卡斯
C b a=C bmodp amodp *C b/p a/p

int qmi(int a, int k)
{
	int res = 1;
	while (k)
	{
		if (k & 1)res = (ll)res * a % p;
		a = (ll)a * a % p;
		k >>= 1;
	}
	return res;
}

int C(int a, int b)
{
	int res = 1;
	for (int i = 1, j = a; i <= b; i++, j--)
	{
		res = (ll)res * j % p;
		res = (ll)res * qmi(i, p - 2) % p;
	}
	return res;
}

int lucas(ll a, ll b)
{
	if (a < p && b < p)return C(a, b);
	return (ll)C(a % p, b % p) * lucas(a / p, b / p) % p;
}

高精度
如果直接算效率太低
分解成p1^a1* p2^a2* pm^am的格式
然后上面减去下面的指数得到最终的
先筛素数,求出每个素数出现的个数 高精度计算

get_primes函数获取a的质因数

void get_primes(int n)
{
	for (int i = 2; i <= n; i++)
	{
		if (!st[i])primes[cnt++] = i;
		for (int j = 0; primes[j] <= n / i; j++)
		{
			st[primes[j] * i] = true;
			if (i % primes[j] == 0)break;
		}
	}
}

gete函数获得指数

int gete(int n, int p)
{
	int res = 0;
	while (n)
	{
		res += n / p;
		n /= p;
	}
	return res;

}

高精度乘法 res*prime[i]

vector<int> mul(vector<int> a, int b)
{
	vector<int> c;
	int t = 0;
	for (int i = 0; i < a.size()||t; i++)
	{
		if (i < a.size()) t += a[i] * b;
		c.push_back(t % 10);
		t /= 10;
	}
	return c;
}

卡特兰数

给定n个0,1按照排列成长度为2n的序列,求他们能排列成的所有序列中,
满足任意前缀序列中0的个数不少于1的序列

用于求前缀中两个数不小于关系
C n 2n -C n-1 2n
C n 2n /n+1

int a=2*n,b=n;
for(int i=a;i>a-b;i--)res=(res)*i%mod;
for(int i=1;i<=b;i++)res=(ll)res*qmi(i,mod-2,mod)

res=(ll)res*qmi(n+1,mod-2,mod)%mod;
cout<<res<<endl;

用递推
1 1 2 5 14 42 132

int a[20]={1};
for(int i=1;i<20;i++)
{
   for(int j=0;j<i;j++)
   {a[i]+=a[j]*a[i-j-1];}
 } 

容斥原理

给定一个整数n和m个不同的质数p1,p2…求出1-n中能被p1-pm中至少一个数整除的整数有几个
求n个集合的并集==一个的全集-两个的全集+三个的全集-四个的全集

位运算的优化
1~1<<m-1 表示了每一个质数选还是不选,j表示第几位

cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		cin >> p[i];
	}
	int res = 0;
	for (int i = 1; i < 1 << m; i++)//一共有2^m-1个数字,i看成一个二进制数,有n位,位上是0表示集合没有被选,1表示被选了
	{
		int t = 1, cnt = 0;//cnt表示包含几个1,有几个集合
		for (int j = 0; j < m; j++)
		{
			if (i >> j & 1)
			{
				cnt++;
				if ((ll)t * p[j] > n)//不用算了c
				{
					t = -1;
					break;
				}
				t *= p[j];
			}
		}
		if (t != -1)
		{
			if (cnt % 2)res += n / t;
			else res -= n / t;
		}
	}
	cout << res << endl;

Nim游戏

有向图游戏:有向无环,唯一起点,两名玩家交替把棋子延有向边移动
nim:给定N个物品,第i堆Ai个,2名玩家轮流获取,每次到任意一堆获取任意多物品,取走最后一件获胜
先手必胜状态:可以走到某个必败状态
a1 ^ a2 ^a3 ^a4=0先手必败 !=0 先手必胜

/*给定n堆石子,两个玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子*/
#include<iostream>
using namespace std;
int main()
{
	int n;
	int res = 0;

	scanf_s("%d", &n);
	while (n--)
	{
		int x;
		scanf_s("%d", &x);
		res ^= x;
	}

	if (res)puts("yes");
	else puts("no");
}

SG函数

有n张图,则SG(x1) ^ SG(x2) ^SG(xn)!=0 必胜,每一个是这张图的起点
sg内部相当于分叉图,每一次存入数,然后从最后的回溯上来,进行mex运算

int sg(int x)
{if(f[x]!=-1)return f[x];
 unordered_set<int>S;
 for(int i=0;i<m;i++)
 { int sum=s[i];
   if(x>=sum)S.insert(x-sum[i]);
   }
   for(int i=0;i;i++)
   if(S.count(i)
   return f[x]=i;
   }
int main()
{
	cin >> m;
	for (int i = 0; i < m; i++)cin >> s[i];
	cin >> n;

	memset(f, -1, sizeof f);
	int res = 0;
	for (int i = 0; i < n; i++)
	{
		int x;
		cin >> x;
		res^= sg(x);
	}
	if (res)puts("yes");
	else puts("no");

	return 0;
}

各种简单背包

分组背包

/*每组物品有若干,同一组中最多只选一个*/
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N][N], w[N][N],s[N];
int f[N];
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> s[i];
		for (int j = 0; j < s[i]; j++)
		{
			cin >> v[i][j] >> w[i][j];
		}
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = m; j >= 0; j--)
		{
			for (int k = 0; k < s[i]; k++)
			{
				if (v[i][k] <= j)
					f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
			}
		}
	}
	cout << f[m] << endl;
	return 0;
}

01背包

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 10010;
int m, n;
int v[N], w[N], f[N][N],dp[N];
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> v[i] >> w[i];
	for (int i = 1; i <= n; i++)
	{
		for(int j=m;j>=v[i];j--)
		{
			dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
		}
	}
	cout << dp[m] << endl;
	return 0;
}

完全背包

	for (int i = 1; i <= n; i++)
	{
		for (int j = v[i]; j <= m; j++)
			dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
	}

多重背包 每个物品可选有限个

for (int i = 1; i <= n; i++)cin >> v[i] >> w[i] >> s[i];

	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= m; j++)
			for (int k = 0; k <= s[i] && k * v[i] <= j; k++)
				f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k);
	}

二进制优化,
把这些全部搞成不同类型背包,然后用01背包做,对k下文章

while(k<=s)
{ cnt++;
 v[cnt]=a*k;
 w[cnt]=b*k;
 s-=k;
 k*=2;
 }
 if(s>0)
 {cnt++;
 v[cnt]=a*s;
 w[cnt]=b*s;
 }
 接下去n=cnt,做01背包

状态压缩

蒙德里安的梦想
自己这一排有限制,和前面一排也有关系
现予处理自己这一排不能有奇数个空,一共有1<<n种可能性,st[i]=true/false;
f[i][j]表示第i列,伸出来的小方块是j的情况数,由f[i-1][k]转移而来,j和k形成的也得满足不能有奇数个空格,同时不能有三个连着的冲突(j&k==0)

	while (cin >> n >> m, n || m)
	{
		memset(f, 0, sizeof f);

		for (int i = 0; i < 1 << n; i++)//预处理对于j的二进制数的0的个数,这个是保证了i+1行里面突出之间有偶数个
		{
			st[i] = true;
			int cnt = 0;
			for (int j = 0; j < n; j++)
			{
				if (i >> j & 1)
				{
					if (cnt & 1)st[i] = false;//cnt有奇数个就不可以有这样的决策
					cnt = 0;
				}
				else cnt++;//当前连续零个数
			}
			if (cnt & 1) st[i] = false;
		}
		f[0][0] = 1;
		for (int i = 1; i <= m; i++)
		{
			for (int j = 0; j < 1 << n; j++)
			{
				for (int k = 0; k < 1 << n; k++)
				{
					if ((j & k) == 0 && st[j | k])
						f[i][j] += f[i - 1][k];
				}
			}
		}
		cout << f[m][0] << endl

区间dp

合并和分割其实区别不大
都是先循环区间长度,在循环区间左端点,再枚举决策·
表示从第i个到第j堆合并成一堆的方式min

	for (int len = 2; len <= n; len++)//区间长度从小到大循环
	{
		for (int i = 1; i + len - 1 <= n; i++)//循环区间左端点
		{
			int l = i, r = len + i - 1;
			f[l][r] = 1e8;
			for (int k = 1; k < r; k++)//枚举决策
				f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
		}
	}

分割

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

const int N=1010;

int f[N][N];
int n,m;
int a[N];
int s[N];

int main()
{
	while(cin>>n,n)
	{
		cin>>m;
		memset(f,0x3f,sizeof f);
		m++;
		for(int i=1;i<m;i++)cin>>a[i];
		a[m]=n;
		for(int i=1;i<=m;i++)
		{
			
			s[i]=a[i]+s[i-1];
			f[i][i]=0;
		}
		
		for(int len=1;len<m;len++)
		for(int l=1;l+len<=m;l++)
		{
			int r=l+len;
			for(int k=l;k<r;k++)
			f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
		}
		
		cout<<"The minimum cutting is "<<f[1][m]<<"."<<endl;
	}
}

最长上升子序列

枚举此之前的为倒数第二,再次之后+1

for(int i=1;i<=n;i++)
{
    f[i]=1;
    for(int j=1;j<i;j++)
    if(a[j]<a[i])
    if(f[i]<f[j]+1)
    f[i]=f[j]+1;
   }

用一个单调队列
二分寻找最大的小于等于这个数的,把它后面一位顶替了,最后输出这个队列长度

for(int i=0;i<n;i++)
{ int l=0,r=len;
while(l<r)
{ int mid=l+r+1>>1;
  if(q[mid]<a[i])l=mid;
  else r=mid-1;
 }
 len=max(len,r+1)
 q[r+1]=a[i]
 }
 cout<<len;

最长公共子序列

f[i][j]所有由第一个序列前i个字母和第二个序列前j个字母组成的子序列
a[i],b[j]是否在这个公共子序列中

	for (int i = 1; i <= n; i++)
  {
  	for (int j = 1; j <= m; j++)
  	{
  		f[i][j] = max(f[i - 1][j], f[i][j - 1]);
  		if (a[i] == b[j])f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
  	}
  }

背包问题求方案数

记录下走到每一步的方案,然后求出下一步的maxv,根据maxv的值,来对于当前步数的进行修改,保留
通过路径确定数量
本质就是最短路问题,如果要字典序最小:贪心

for (int i = 0; i < n; i++)
	{
		int w, v;
		cin >> v >> w;
		for (int j = m; j >= v; j--)
		{
			int maxv = max(f[j], f[j - v] + w);
			int cnt = 0;
			if (maxv == f[j]) cnt += g[j];
			if (maxv == f[j - v] + w)cnt += g[j - v];
			g[j] = cnt % mod;
			f[j] = maxv;
		}
	}
	int res = 0;
	for (int i = 0; i <= m; i++)res = max(res, f[i]);
	int cnt = 0;
	for (int i = 0; i <= m; i++)
		if (res == f[i])
			cnt = (cnt + g[i]) % mod;

背包问题求具体方案数目

这种就不可以再状态压缩了,所以第一次的两重循环顺序无所谓了
先求出来最大的数目
再从头开始推,看它来自哪里

拦截导弹的贪心

只能拦截比他矮的,问需要几个系统
如果现有子序列的结尾要小于它,就创新的
如果不是就放在后面
每一个存在于这个序列里的数字,就是一个系统,表这个系统的最小的末尾,while找到第一个大于他的系统

for (int i = 0; i < n; i++)
	{
		int k = 0;
		while (k < cnt && g[k] < q[i])k++;
		g[k] = q[i];
		if (k >= cnt)cnt++;
	}

体积的思考(潜水员)

体积最多j:全部初始化为0,v>=0;
体积恰好j:f[0]=0,f[i]=+inf v>=0
体积至少为j:f[0]=0,f[i]=+inf (为负是有方案的)

	int v1, v2, w;
		cin >> v1 >> v2 >> w;
		for (int i = n; i >= 0; i--)
		{
			for (int j = m; j >= 0; j--)
			{
				f[i][j] = min(f[i][j], f[max(0, i - v1)][max(0, j - v2)] + w);
			}
		}

用前面的能不能表达后面的(货币系统)

先输入然后排序,从小到大
f[0]=1
从小到大,列举各个体积,已有了a[i],则f[j]+=f[j-a[i]](这个j除了a的其他可以被表示就可以被表示)

	cin >> n;
		for (int i = 0; i < n; i++) cin >> a[i];
		sort(a, a + n);

		int m = a[n - 1];
		memset(f, 0, sizeof f);
		f[0] = 1;

		int res = 0;
		for (int i = 0; i < n; i++)
		{
			if (!f[a[i]])res++;
			for (int j = a[i]; j <= m; j++)f[j] += f[j - a[i]];
		}
		cout << res << endl;

有依赖的背包

就是一棵树,儿子结点依赖于根节点,dfs遍历到叶结点,然后回溯
总体思路还是三重循环,循环物品(不同的子结点),循环体积,循环决策(儿子有多少)

void dfs(int u)
{
	for (int i = h[u]; ~i; i = ne[i])
	{
		int son = e[i];
		dfs(e[i]);

		//分组背包
		for (int j = m - v[u]; j >= 0; j--)//循环体积
		{
			for (int k = 0; k <= j; k++)//循环决策
			{
				f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);
			}
		}
	}

	//将物品u加进去
	for (int i = m; i >= v[u]; i--)f[u][i] = f[u][i - v[u]] + w[u];
	for (int i = 0; i < v[u]; i++)f[u][i] = 0;

}
dfs(root)
cout<<<f[root][m]<<endl;

状态机(叽叽叽)

多开一维数组来表示状态
每一次循环里面,对于两种状态进行取值

	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			f[i][j][0] = max(f[i - 1][j][0], f[i - 1][j][1] + w[i]);
			f[i][j][1] = max(f[i - 1][j][1], f[i - 1][j - 1][0] - w[i]);

		}
	}

状态压缩DP

细讲棋盘式
对于每一排存储成一个二进制数,表示这个坑填还是不填(用于某些坑要跳过的题目筛查时用),对于每一种排列(0-1<<m-1)进行check,并把满足条件的保存到state数组内

两次循环可能的来进行关系的判断,满足的可以添加到以a为头的可能数组中

不能有连续两个相邻state>>1&1&&state>>i+1&1

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;
const int N = 14, M = 1 << 12, mod = 1e8;

int n, m;
int g[N];
vector<int>state;
vector<int>head[M];
int f[N][M];

bool check(int state)
{
	for (int i = 0; i < m; i++)
	{
		if ((state >> 1 & 1) && (state >> i + 1 & 1))
			return false;
	}
	return true;
}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			int t;
			cin >> t;
			g[i] += !t << j;
		}
	}
	for (int i = 0; i < 1 << m; i++)
	{
		if (check(i))
			state.push_back(i);
	}
	for (int i = 0; i < state.size(); i++)
	{
		for (int j = 0; j < state.size(); j++)
		{
			int a = state[i], b = state[b];
			if ((a & b) == 0)
				head[i].push_back(j);
		}
	}
	f[0][0] = 1;
	for (int i = 1; i <= n + 1; i++)
	{
		for (int a = 0; a < state.size(); a++)
		{
			for (int b : head[a])
			{
				if (g[i] & state[a])continue;
				f[i][a] = (f[i][a] + f[i - 1][b]) % mod;
			}
		}
	}
	cout << f[n + 1][0] << endl;
	return 0;
}

树的直径

任取一个点作为起点,寻找距离该点最远的一点,再找距离它最远的一个点,两点间距离就是直径,要维护最长和次长
主要利用深搜,返回是到最下面叶结点的距离

int dfs(int u,int father)
{ int dist=0;
  int d1=0,d2=0;
  for(int i=h[u];~i;i=ne[i])
  {    int j=e[i];
  if(j==father)continue;
  int d=dfs(j,u)+w[i];
  dist=max(dist,d);
  if (dist >= d1)d2 = d1, d1 = dist;
  else if (dist > d2)d2 = dist;
	}
	ans = max(ans, d1 + d2);
	return dist;

树形dp

往往是dfs
dfs(1,-1)
先深搜,在回溯,根据回溯的进行dp
思考根和节点之间关系 寻找u和j之间关系

void dfs(int u)
{
	f[u][0] = 0;
	f[u][1] = 1;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		dfs(j);

		f[u][0] += f[j][1];
		f[u][1] += min(f[j][0], f[j][1]);
	}
}

有些状态复杂,但可以通过其他状态推的,先把简单的找出来,再通过一次把复杂的需要有处理结果支持的处理出来
重点是分析处理出来父亲节点状态和儿子节点状态的关系

void dfs(int u)
{
	f[u][2] = w[u];
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		dfs(j);

		f[u][0]+= min(f[j][1], f[j][2]);
		f[u][2] += min(min(f[j][0], f[j][1]), f[j][2]);

	}
	f[u][1] = -1e9;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		f[u][1] = min(f[u][1], f[j][2] + f[u][0] - min(f[j][1], f[j][2]));
	}
}

数位dp

求区间看成前缀和之差
f[第几位][最高的数字/上一位留下的需要满足的东西]
把每一位数字拆分开1存入数组,从最高位开始循环,res+满足的处理好的dp数量
把这个数但单独提出来,循环比他小的
分为1-这个数小1,和取这个数位本身的数
最后处理这个数字本身算不算答案
初始化处理的时候只有1位的答案要单独赋值
不要62

void init()
{
	for (int i = 0; i <= 9; i++)
	{
		if (i != 4)
			f[1][i] = 1;
	}

	for(int i=2;i<N;i++)
		for (int j = 0; j <= 9; j++)
		{
			if (j == 4)continue;
			for (int k = 0; k <= 9; k++)
			{
				if (k == 4 || j == 6 && k == 2)continue;
				f[i][j] += f[i - 1][k];
			}
	   }
}

int dp(int n)
{
	if (!n)return 1;
	vector<int>nums;
	while (n)nums.push_back(n % 10),n /= 10;

	int res = 0;
	int last = 0;

	for (int i = nums.size() - 1; i >= 0; i--)
	{
		int x = nums[i];
		for (int j = 0; j < x; j++)
		{
			if (j == 4 || last == 6 && j == 2)continue;
			res += f[i + 1][j];
		}
		if (x == 4 || last == 6 && x == 2)break;

		last = x;

		if (!i)res++;
	
	}

}

不降数

void init()
{
	for (int i = 0; i <= 9; i++)f[1][i] = 1;

	for (int i = 2; i < N; i++)
		for (int j = 0; j <= 9; j++)
			for (int k = j; k <= 9; k++)
				f[i][j] += f[i - 1][k];
}
int dp(int n)
{
	if (!n)return 1;

	vector<int>nums;
	while (n)nums.push_back(n % 10), n /= 10;

	int res = 0;
	int last = 0;

	for (int i = nums.size() - 1; i >= 0; i--)
	{
		int x = nums[i];
		for (int j = last;j < x; j++)
		{
				res += f[i + 1][j];
		}
		if (x < last) break;
		last = x;
		if (!i)res++;
	
	}
	return res;
}

单调队列优化DP

举个例子:某两个城市之间有n个烽火台,连续m个中至少有一个要放出信号,求总代价
f[i]表示点燃第i个的合理最少数量 f[i]=min(f[j])+wi
所以要求在区间长度为m中的最小值

循环 每一次先判断头部出没出队,更新最新头部,得出f[i],再将这个新的值加入到队列中

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> w[i];

	int hh = 0, tt = 0;
	for (int i = 1; i <= n; i++)
	{
		if (q[hh] < i - m)hh++;
		f[i] = f[q[hh]] + w[i];
		while (hh <= tt && f[q[tt]] >= f[i])tt--;
		q[++tt] = i;
	}
	int res = 1e9;
	for (int i = n - m + 1; i <= n; i++)res = min(res, f[i]);

	cout << res;
	return 0;
}

最大子序列和

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> s[i];
		s[i] += s[i - 1];
	}

	int res = 0;
	int hh = 0, tt = 0;
	for (int i = 1; i <= n; i++)
	{
		if (q[hh] < i - m)hh++;
		res = max(res, s[i] - s[q[hh]]);
		while (hh << tt && s[q[tt]] >= s[i])tt--;//找小的捏
		q[++tt] = i;
	}
	cout << res;
	return 0;
}

FLOOD FILL

没有什么好说的其实就是设置st,队列,方向变量
放一道城堡问题

#include<iostream>
#include<cstring>
#include<algorithm>

#define x first
#define y second

using namespace std;
typedef pair<int, int>PII;

const int N = 55, M = N * N;

int n, m;
int g[N][N];
PII q[M];
bool st[N][N];
int bfs(int sx, int sy)
{
	int dx[4] = { 0,-1,0,-1 }, dy[4] = { -1,0,1,0 };

	int hh = 0, tt = 0;
	int area = 0;
	q[0] = { sx,sy };
	st[sx][sy] = true;

	while (hh <= tt)
	{
		PII t = q[hh++];
		area++;

		for (int i = 0; i < 4; i++)
		{
			int a = t.x + dx[i], b = t.y + dy[i];
			if (a < 0 || a >= n || b < 0 || b >= m)continue;
			if (st[a][b])continue;
			if (g[t.x][t.y] >> 1 & 1)continue;

			q[++tt] = { a,b };
			st[a][b] = true;
		}
	}
	return area;
}
int main()
{
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			cin >> g[i][j];

	int cnt = 0, area = 0;
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			if (!st[i][j])
			{
				area = max(area, bfs(i, j));
				cnt++;
			}
	cout << cnt << endl;
	cout << area << endl;
	return 0;
}

多元BFS

具有两段性,具有单调性
魔板:广搜输入start,有三种不同的string,对此循环,如果从来没有走过这种这种状态,就把走到这种的距离增加1,并且加入队列

/*在成功地发明了魔方之后,拉比克先生发明了它的二维版本,称作魔板。这是一张有8个大小相同的格子的魔板:
1 2 3 4
8 7 6 5
  我们知道魔板的每一个方格都有一种颜色。
  这8种颜色用前8个正整数来表示。
  可以用颜色的序列来表示一种魔板状态,
  规定从魔板的左上角开始,沿顺时针方向依次取出整数,
  构成一个颜色序列。对于上图的魔板状态,
  我们用序列(1,2,3,4,5,6,7,8)来表示。这是基本状态。
  这里提供三种基本操作,分别用大写字母“A”,“B”,“C”来表示
  (可以通过这些操作改变魔板的状态):
“A”:交换上下两行;
“B”:将最右边的一列插入最左边;
“C”:魔板中央四格作顺时针旋转。
  下面是对基本状态进行操作的示范:
A: 8 7 6 5
1 2 3 4
B: 4 1 2 3
5 8 7 6
C: 1 7 2 4
8 6 3 5
  对于每种可能的状态,这三种基本操作都可以使用。
  你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。

Input*/
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<queue>

using namespace std;

char g[2][4];
unordered_map<string, int>dist;
unordered_map<string, pair<char, string> >pre;
queue<string>q;

void set(string state)
{
	for (int i = 0; i < 4; i++)g[0][i] = state[i];
	for (int i = 3, j = 4; i >= 0; i--, j++)g[1][i] = state[j];
}
string get()
{
	string res;
	for (int i = 0; i < 4; i++)res += g[0][i];
	for (int i = 3; i >= 0; i--)res += g[1][i];
	return res;
}
string move0(string state)
{
	for (int i = 0; i < 4; i++)g[0][i] = state[i];
	for (int i = 3, j = 4; i >= 0; i--, j++)g[1][i] = state[i];
}
string move1(string state)
{
	set(state);
	char v0 = g[0][3], v1 = g[1][3];
	for (int i = 3; i > 0; i--)
		for (int j = 0; j < 2; j++)
			g[j][i] = g[j][i - 1];
	g[0][0] = v0, g[1][0] = v1;
	return get();
}
string move2(string state)
{
	set(state);
	char v = g[0][1];
	g[0][1] = g[1][1];
	g[1][1] = g[1][2];
	g[1][2] = g[0][2];
	g[0][2] = v;
	return get();
}
void bfs(string start, string end)
{
	if (start == end)return;

	q.push(start);
	dist[start] = 0;

	while (q.size())
	{
		auto t = q.front();
		q.pop();

		string m[3];
		m[0] = move0(t);
		m[1] = move1(t);
		m[2] = move2(t);

		for (int i = 0; i < 3; i++)
		{
			string str = m[i];
			if (dist.count(str) == 0)
			{
				dist[str] = dist[t] + 1;
				pre[str] = { char(i + 'A'),t };
				if (str == end)break;
				q.push(str);
			}
		}
	}
}
int main()
{
	int x;
	string start, end;
	for (int i = 0; i < 8; i++)
	{
		cin >> x;
		end += char(x + '0');
	}

	for (int i = 0; i < 8; i++)start += char(i + '1');

	bfs(start, end);

	cout << dist[end] << endl;

	string res;
	while (end != start)
	{
		res += pre[end].first;
		end = pre[end].second;
	}

	reverse(res.begin(), res.end());

	if (res.size())cout << res << endl;

	return 0;
}

双向广搜(解决最小步数)

电路维修
两位坐标存pair
用双端队列
对于可以一步到达的d=dist,需要转一下的d=dist+1,并且存到队尾
0,1的广搜都可以用双端队列

#include<cstring>
#include<iostream>
#include<algorithm>
#include<deque>

#define x first
#define y second

using namespace std;

typedef pair<int, int>PII;

const int N = 510, M = N * N;

int n, m;
char g[N][N];
int dist[N][N];
bool st[N][N];

int bfs()
{
	deque<PII>q;
	memset(st, 0, sizeof st);
	memset(dist, 0x3f, sizeof dist);

	char cs[5] = "\\/\\/";
	int dx[4] = { -1,-1,1,1 }, dy[4] = { -1,1,1,-1 };
	int ix[4] = { -1,-1,0,0 }, iy[4] = { -1,0,0,-1 };

	dist[0][0] = 0;
	q.push_back({ 0,0 });

	while (q.size())
	{
		auto t = q.front();
		q.pop_front();

		int x = t.x, y = t.y;
		if (x == n && y == m)return dist[x][y];

		if (st[x][y])continue;
		st[x][y] = true;

		for (int i = 0; i < 4; i++)
		{
			int a = x + dx[i], b = y + dy[i];
			if (a<0 || a>n || b<0 || b>m)continue;
			int ga = x + ix[i], gb = y + iy[i];
			int w = (g[ga][gb] != cs[i]);//相等就是0
			int d = dist[x][y] + w;
			if (d <= dist[a][b])
			{
				dist[a][b] = d;
				if (!w)q.push_front({ a,b });
				else q.push_back({ a,b });
			}
		}
	}
	return -1;
}

int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		cin >> n >> m;
		for (int i = 0; i < n; i++)cin >> g[i];

		if (n + m & 1)puts("NO SOLUTION");
		else cout << bfs();
	}
	return 0;
}

A*算法

估计距离必须大于真实距离
bfs入队判重,digkstra出队判重,A*不能判重

一个unordered_map存状态string对应的距离
优先队列里面存估计距离和状态(根据估计距离的大小来判断先走哪一个状态)
while中利用这个状态,找到x,进行方向上的转换,如果这种状态再dist中没有,或者距离更短,则存进来,(dist和heap(heap里面存的是现在的距离和估值距离)

#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<queue>

#define x fisrt
#define y second

using namespace std;

typedef pair<int, string> PIS;

int f(string state)
{
	int res = 0;
	for (int i = 0; i < state.size(); i++)
		if (state[i] != 'x')
		{
			int t = state[i] -'1';
			res += abs(i / 3 - t / 3) + abs(i % 3 - t % 3);
		}
	return res;
}

string bfs(string start)
{
	string end = "12345678x";
	char op[] = "urdl";

	unordered_map<string, int> dist;
	unordered_map<string, pair<char, string> >prev;
	priority_queue<PIS, vector<PIS>, greater<PIS> >heap;

	dist[start] = 0;
	heap.push({ f(start),start });

	int dx[4] = { -1,0,1,0 }, dy[4] = { 0,1,0,-1 };

	while (heap.size())
	{
		auto t = heap.top();
		heap.pop();

		string state = t.y;
		if (state == end) break;

		int x, y;
		for(int i=0;i<9;i++)
			if (state[i] == 'x')
			{
				x = i / 3, y = i % 3;
				break;
			}

		string source = state;
		for (int i = 0; i < 4; i++)
		{
			int a = x + dx[i], b = y + dy[i];
			if (a < 0 || a >= 3 || b<0 || b>=3)continue;
			state = source;
			swap(state[x * 3 + y], state[a * 3 + b]);
			if (dist.count(state) == 0 || dist[state] > dist[source] + 1)
			{
				dist[state] = dist[source] + 1;
				prev[state] = { op[i],source };
				heap.push({ dist[state] + f(state),state });
			}
		}
	}

	string res;
	while (end != start)
	{
		res += prev[end].first;
		end = prev[end].y;
	}
	reverse(res.begin(), res.end());
	return res;

}

int main()
{
	string start, seq;
	char c;

	while (cin >> c)
	{
		start += c;
		if (c != 'x')seq += c;
	}

	int cnt = 0;
	for(int i=0;i<8;i++)
		for (int j = i; j < 8; j++)
		{
			if (seq[i] > seq[j])
				cnt++;
		}
	if (cnt & 1)puts("unsolvable");
	else cout << bfs(start) << endl;

	return 0;
}

第几次弹出就是第几小路

DFS

利用栈来搜索
任意找到一个起点,然后进行深搜,cnt+=dfs(a,b)
相当于先遍历到最后一个点,然后在返回,每一次都把个数+1
例子:红与黑

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 25;

int n, m;
char g[N][N];
bool st[N][N];

int dx[4] = { -1,0,1,0 }, dy[4] = { 0,1,0,-1 };

int dfs(int x, int y)
{
	int cnt = 1;

	st[x][y] = true;
	for (int i = 0; i < 4; i++)
	{
		int a = x + dx[i], b = y + dy[i];
		if (a < 0 || a >= n || b < 0 || b >= n)continue;
		if (g[a][b] != '.')continue;
		if (st[a][b])continue;

		cnt += dfs(a, b);
	}
	return cnt;
}

int main()
{
	while (cin >> m >> n, n || m)
	{
		for (int i = 0; i < n; i++)cin >> g[i];

		int x, y;
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (g[i][j] == '@')
				{
					x = i;
					y = j;
				}
			}
		}
		memset(st, 0, sizeof st);
		cout << dfs(x, y) << endl;
	}
}

用dfs来枚举,就是选择了一种情况,并再次条件下搜索下去
例子:分为互质组
dfs(第几组,当前组内下标,当前多少元素,从哪个开始搜索)
如果可以放进去,组内下标+1,元素+1,哪一个+1
无论如何都可以不放 第几组+1,组内下标=0,当前元素不变,从0开始

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 10;

int n;
int p[N];
int group[N][N];
bool st[N];
int ans = N;

int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
bool check(int group[], int gc, int i)
{
	for (int j = 0; j < gc; j++)
	{
		if (gcd(p[group[j]], p[i]) > 1)
			return false;
	}
	return true;
}
void dfs(int g, int gc, int tc, int start)//第几组,当前组内下标,当前多少元素,从何

{
	if (g>= ans)return;
	if (tc == n)ans = g;

	bool flag = true;
	for (int i = start; i < n; i++)
		if(!st[i]&&check(group[g],gc,i))
	{
		st[i] = true;
		group[g][gc] = i;
		dfs(g, gc + 1, tc + 1, i + 1);
		st[i] = false;

		flag = false;
	}
	if (flag) dfs(g + 1, 0, tc, 0);
}
int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)cin >> p[i];

	dfs(1, 0, 0, 0);

	cout << ans;
	return 0;
}

剪枝大法

  • 优化搜索顺序(优先搜索分支小的)
  • 排除等效冗余
  • 可行性剪枝
  • 最优性剪枝

小猫爬山

依次枚举每只小猫(用dfs里的参数),枚举把当前小猫放到某辆车上(循环k)
剪枝

  • 优先放重猫,可以减少枚举的数量,把很多从一开 始就不满足放
  • 超出重量直接砍
  • >=ans
    dfs(第几只猫,第几组)
    每一次枚举完要还原
void dfs(int u, int k)//the number of cat,the number of cable
{
   //最优性
   if (k >= ans)return;
   if (u == n)
   {
   	ans = k;
   	return;
   }

   for (int i = 0; i < k; i++)
   {
   	if (sum[i] + w[u] <= m)
   	{
   		sum[i] += w[u];
   		dfs(u + 1, k);
   		sum[i] -= w[u];
   	}
   }

   sum[k] = w[u];
   dfs(u + 1, k + 1);
   sum[k] = 0;

}

木棒

长度从小到大枚举
dfs(组数,当前枚举的长度,开始)

  • length/sum
  • 先枚举长木棍
  • 排除冗余
  • 组合数枚举(不用管顺序,下标从小到大枚举)
  • 当前失败,则相同长度的也一定失败
  • 第一个失败当前length失败
  • 最后一个失败 当前length失败
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 70;

int n;
int w[N], sum, length;
bool st[N];
bool dfs(int u, int s, int start)//组数,当前长度,开始
{
   if (u * length == sum)return true;
   if (s == length)return (u + 1, 0, 0);

   //i从start枚举
   for (int i = start; i < n; i++)
   {
   	if (st[i])continue;
   	if (s + w[i] > length)continue;

   	st[i] = true;
   	if (dfs(u, s + w[i], i + 1))return true;
   	st[i] = false;

   	//第一个失败一定搜不到
   	if (!s)return false;

   	//最后一个失败这个length一定搜不到
   	if (s + w[i] == length)return false;

   	int j = i;
   	while (j < n && w[j] == w[i])j++;
   	i = j - 1;
   }
   return false;
}
int main()
{
   while (cin >> n, n)
   {
   	memset(st, 0, sizeof st);
   	sum = 0;

   	for (int i = 0; i < n; i++)
   	{
   		cin >> w[i];
   		sum += w[i];
   	}

   	sort(w, w + n);
   	reverse(w, w + n);

   	length = 1;
   	while (true)
   	{
   		if (sum % length == 0 && dfs(0, 0, 0))
   		{
   			cout << length << endl;
   			break;
   		}
   		length++;
   		if (length > sum)break;
   	}
   }
   return 0;
}

生日蛋糕

  • 从底向上搜,先r后h
  • 预处理出来最小的r和h,保证v+min(v)<=N,s+min(s)<ans
  • r和h枚举的时候是由范围的
  • 神奇的一个式子
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

const int N = 25, INF = 1e9;

int n, m;
int minv[N], mins[N];
int R[N], H[N];
int ans = INF;

void dfs(int u, int v, int s)
{
   if (v + minv[u] > n)return;
   if (s + mins[u] >= ans)return;
   if (s + 2 * (n - v) / R[u + 1] >= ans)return;

   if (!u)
   {
   	if (v == n)ans = s;
   	return;
   }
   for (int r = min(R[u + 1] - 1, (int)sqrt(n - v)); r >= u; r--)
   {
   	for (int h = min(H[u + 1] - 1, (n - v) / r / r); h >= u; h--)
   	{
   		int t = 0;
   		if (u == m)t = r * r;
   		R[u] = r, H[u] = h;
   		dfs(u - 1, v + r + r * h, s + 2 * r * h + t);
   	}
   	
   }
}
int main()
{
   cin >> n >> m;

   for (int i = 1; i <= m; i++)
   {
   	minv[i] = minv[i - 1] + i * i * i;
   	mins[i] = mins[i - 1] + 2 * i * i;
   }

   R[m + 1] = H[m + 1] = INF;

   dfs(m, 0, 0);

   cout << ans << endl;

   return 0;
}

迭代加深

设置一个搜索上线,到了这个深度,就返回,适用于搜索深度深但是答案在浅层次的

加成序列
对于每个k,都存在两个整数使得 X[k]=X[i]+X[j]
X[1] X[m]=n 求最小的m
剪枝

  • 优先枚举较大数
  • 排除等效冗余
  • 迭代加深
int depth =1;
while(!dfs(1,depth) )depth++;

bool dfs(int u, int depth)
{
	if (u > depth)return false;
	if (path[u - 1] == n)return true;

	bool st[N] = { 0 };
	for (int i = u - 1; i >= 0; i--)
	{
		for (int j = i; j >= 0; j--)
		{
			int s = path[i] + path[j];
			if (s > n || s <= path[u - 1] || st[s]) continue;

			st[s] = true;
			path[u] = s;
			if (dfs(u + 1, depth))return true;
		}
	}
	return false;
}

空间换时间的二分

N个礼物,第i个的重量是G[i],一次可以放入重量之和不超过w的任意多个物品,求一次性能放入的最大重量

如果直接爆搜全部,也就是说直接枚举每个物品放不放到包里,就是2^23
所以可以先搜一半,打表打出来,后来就可以直接查询,在搜后一半然后查出来小于等于w-s的最大值,时间复杂度是2^12.5 + 2^12.5*n/2

dfs1(第几个,重量)
dfs2(第几个,重量)

di'ji'g#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long LL;
const int N = 46;

int n, m, k;
int w[N];
int weights[1 << 25], cnt = 1;
int ans;

void dfs1(int u, int s)//第几个,重量
{
   if (u == k)
   {
   	weights[cnt++] = s;
   	return;
   }

   dfs1(u + 1, s);//不放
   if ((LL)s + w[u] <= m)dfs1(u + 1, s + w[u]);//放
   
}

void dfs2(int u, int s)
{
   if (u >= n)//枚举到这里了
   {
   	int l = 0,r = cnt - 1;
   	while (l < r)
   	{
   		int mid = l + r + 1 >> 1;
   		if (weights[mid] <= m - s)l = mid;
   		else r = mid - 1;
   	}
   	ans = max(ans, weights[l] + s);
   	return;
   }

   dfs2(u + 1, s);
   if ((LL)s + w[u] <= m) dfs2(u + 1, s + w[u]);
}
int main()
{
   cin >> m >> n;
   for (int i = 0; i < n; i++)
   	cin >> w[i];

   sort(w, w + n);
   reverse(w, w + n);

   k = n / 2 + 2;
   dfs1(0, 0);

   sort(weights, weights + cnt);
   cnt = unique(weights, weights + cnt) - weights;

   dfs2(k, 0);

   cout << ans << endl;
   return 0;
}

IDA*

估价函数<=真实
dfs(当前深度,最高深度)
if depth+f()>max_depth return false;
if f()==0 return true;

举个例子:排书

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 15;

int n;
int q[N];
int w[5][N];

int f()
{
	int tot = 0;
	for (int i = 0; i + 1 < n; i++)
	{
		if (q[i + 1] != q[i] + 1)
			tot++;
	}
	return (tot + 2) / 3;//每一次更改三个后继,所以是tot/3上取整
}
bool dfs(int depth, int max_depth)
{
	if (depth + f() > max_depth)return false;
	if (f() == 0)return true;

	for (int len = 2; len <= n; len++)
	{
		for (int l = 0; l + len - 1 < n; l++)
		{
			int r = l + len - 1;
			for (int k = r + 1; k < n; k++)//向后差在哪里
			{
				memcpy(w[depth], q, sizeof q);
				int y = l;
				for (int x = r + 1; x <= k; x++, y++)q[y] = w[depth][x];
				for (int x = l; x <= r; x++, y++)q[y] = w[depth][x];
				if (dfs(depth + 1, max_depth))return true;
				memcpy(q, w[depth], sizeof q);
			}
		}
	}
	return false;

}
int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		cin >> n;
		for (int i = 0; i < n; i++)cin >> q[i];

		int depth = 0;
		while (depth < 5 && !dfs(0, depth))depth++;

		if (depth >= 5)puts("5 or more");
		else cout << depth << endl;
	}
	return 0;
}

并查集

一共有两个操纵:
合并两个集合
查询某元素的祖宗节点
可以维护

  • 记录每一个集合的的大小(绑定到根节点上)
  • 每个点到根节点的距离

举个例子:搭配购买
把搭配的合并成一个物品,只看root点

	while (m--)
	{
		int a, b;
		cin >> a >> b;
		int pa = find(a), pb = find(b);
		if (pa != pb)
		{
			v[pb] += v[pa];
			w[pb] += w[pa];
			p[pa] = p[pb];
		}
	}

	for (int i = 1; i <= n; i++)
	{
		if (p[i] == i)
		{
			for (int j = vol; j >= v[i]; j--)
				f[j] = max(f[j], f[j - v[i]] + w[i]);
		}
	}
	cout << f[vol] << endl;

银河英雄传说
维护到根节点距离
当两个集合合并时候,一个到根节点的距离就是size[b],同时size[b]要更新

while (m--)
	{
		char op[2];
		int a, b;
		cin >> op >> a >> b;
		if (op[0] == 'M')
		{
			int pa = find(a), pb = find(b);
			d[pa] = SIZE[pb];
			SIZE[pb] += SIZE[pa];
			p[pa] = pb;
		}
		else
		{
			int pa = find(a), pb = find(b);
			if (pa != pb)puts("-1");
			else cout << abs(d[a] - d[b]) - 1<<endl;
		}
	}

奇数偶数游戏

小 A和小 B在玩一个游戏。首先,小 A
写了一个由 0和 1组成的序列 S,长度为 N。
然后,小 B向小 A提出了 M个问题
在每个问题中,小 B 指定两个数 l和 r,小 A回答 S[l∼r]中有奇数个 1还是偶数个 1。
机智的小 B发现小 A 有可能在撒谎。
例如,小 A曾经回答过 S[1∼3]中有奇数个 1,S[4∼6]中有偶数个 1,现在又回答 S[1∼6]中有偶数个 1,显然这是自相矛盾的。
请你帮助小 B 检查这 M个答案,并指出在至少多少个回答之后可以确定小 A一定在撒谎。
即求出一个最小的 k,使得 01序列 S满足第 1∼k个回答,但不满足第 1∼k+1个回答。

边带权做法
运用前缀和,求出来前面i个数字的1的个数
于是Sr-Sl-1是个奇数就说明了两者的奇数偶数性不同,反之相同

用d[x]表示x与p[x]关系(0表示是同类,1表示不同类)
d[x]+d[y]是奇数说明奇数偶数性不同,反之则相同

if(x,y是同类)
//if(已经在一个树里面)
dx^dy=0无矛盾
//if(不在一棵树上)
dpx=dx^dy ^0 (dx+dpx+dy=0(偶数))

#include <cstdio>
#include <cstring>
#include <iostream>
#include <unordered_map>

using namespace std;

const int N = 2e4 + 10;

int n, m;
int p[N], d[N];
unordered_map<int, int> S;

int get(int x) {
    if (S.count(x) == 0) {
        S[x] = ++n;
    }
    return S[x];
}

int find(int x) {
    if (p[x] != x) {
        int root = find(p[x]);
        d[x] ^= d[p[x]];
        p[x] = root;
    }
    return p[x];
}

int main() {
    cin >> n >> m;
    n = 0;

    for (int i = 0; i < N; i++) {
        p[i] = i;
    }

    int res = m;  //如果无矛盾, 输出问题数量, 初始的时候为m
    for (int i = 1; i <= m; i++) {
        int a, b;
        string type;
        cin >> a >> b >> type;
        a = get(a - 1), b = get(b);  // s[a-1], s[b]

        int t = 0;
        if (type == "odd") {
            t = 1;
        }
        int pa = find(a), pb = find(b);
        if (pa == pb) {
            if ((d[a] ^ d[b]) != t) {
                res = i - 1;
                break;
            }
        }
        else {
            p[pa] = pb;
            d[pa] = d[a] ^ d[b] ^ t;
        }
    }
    cout << res << endl;
}

扩展域
把各个条件放到一个集合内,该集合内的一个成立,全部成立
if(x,y是同类)合并x+n,y+n,合并x,y;
反之 反之

#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>

using namespace std;

const int N = 40010, Base = N / 2;

int n, m;
int p[N];
unordered_map<int, int>S;

int get(int x)
{
	if (S.count(x) == 0)S[x] = ++n;
	return S[x];
}
int find(int x)
{
	if (p[x] != x)p[x] = find(p[x]);
	return p[x];
}
int main()
{
	cin >> n >> m;
	n = 0;

	for (int i = 1; i <= m; i++)p[i] = i;

	int res = m;
	for (int i = 0; i < N; i++)
	{
		int a, b;
		string type;
		cin >> a >> b >> type;
		a = get(a - 1), b = get(b);

		if (type == "even")
		{
			if (find(a + Base) == find(b))
			{
				res = i - 1;
				break;
			}
			p[find(a)] = find(b);
			p[find(a + Base)] = find(b + Base);
		}
		else
		{
			if (find(a) == find(b))
			{
				res = i - 1;
				break;
			}

			p[find(a + Base)] = find(b);
			p[find(a)] = find(b + Base);
		}
	}
	cout << res << endl;

	return 0;

}

单源最短路的拓展

虚拟源点

适用于没有起点或者是多个起点的问题
举个例子:聘礼
设置一个虚拟源点,用来表示他自己本身需要的钱,也就是w[0][i]=price
枚举不同的区间,当放松的时候必须满足区间大小才可以,用dijkstra找到达0的最近距离

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 110, INF = 0x3f3f3f3f;

int n, m;
int w[N][N], level[N];
int dist[N];
bool st[N];

int dijkstra(int down ,int up)
{
	memset(dist, 0x3ff, sizeof dist);
	memset(st, 0, sizeof st);

	dist[0] = 0;
	for (int i = 1; i <= n+1; i++)
	{
		int t = -1;
		for (int j = 0; j <= n; j++)
			if (!st[j] && (t == -1 || dist[t] > dist[j]))
				t = j;
		st[t] = true;

		for (int j = 1; j <= n; j++)
			if (level[j] >= down && level[j] <= up)
				dist[j] = min(dist[j], dist[t] + w[t][j]);
	}
	return dist[1];


}
int main()
{
	cin >> m >> n;

	memset(w, 0x3f, sizeof w);
	for (int i = 1; i <= n; i++)w[i][i] = 0;

	for (int i = 1; i <= n; i++)
	{
		int price, cnt;
		cin >> price >> level[i] >> cnt;
		w[0][i] = min(price, w[0][i]);
		while (cnt--)
		{
			int id, cost;
			cin >> id >> cost;
			w[id][i] = min(w[id][i], cost);
		}
	}
	int res = INF;

	for (int i = level[1] - m; i <= level[1]; i++)res = min(res, dijkstra(i, i + m));
	cout << res << endl;

	return 0;
}

预处理单元最短距离

举个例子:新年好
如果暴力枚举各种走的顺序 超时
先预处理出来每一个走到其他点的最短距离,在枚举从谁出发
深搜d(第几个,当前是,距离)内部循环

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 50010, M = 200010, INF = 0x3f3f3f3f;
int n, m;
int source[6];
int h[N], ne[N], w[N], e[N], idx;
int q[N], dist[6][N];
bool st[N];
void spfa(int start, int dist[])
{
	memset(dist, 0x3f, N * 4);
	dist[start] = 0;

	int hh = 0, tt = 1;
	q[0] = start;

	while (hh != tt)
	{
		int t = q[hh++];
		if (hh == N)hh = 0;

		st[t] = false;
		for (int i = h[t]; ~i; i = ne[i])
		{
			int j = e[i];
			if (dist[j] > dist[t] + w[i])
			{
				dist[j] = dist[t] + w[i];
				if (!st[j])
				{
					q[tt++] = j;
					if (tt == N)tt = 0;
					st[j] = true;
				}
			}
		}
	}
}
int dfs(int u, int start, int distance)
{
	if (u == 6)return distance;//每一次返回

	int res = INF;
	for (int i = 1; i <= 5; i++)
	{
		if (!st[i])
		{
			int next = source[i];
			st[i] = true;
			res = min(res, dfs(u + 1, i, distance + dist[start][next]));
			st[i] = false;
		}
	}
	return res;//终极返回
}
void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a];
	h[a] = idx++;
}
int main()
{
	cin >> n >> m;
	source[0] = 1;
	for (int i = 1; i <= 5; i++)cin >> source[i];

	memset(h, -1, sizeof h);
	while (m--)
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c), add(b, a, c);
	}
	for (int i = 0; i < 6; i++)spfa(source[i], dist[i]);
	cout << dfs(1, 0, 0);
	return 0;
}

与二分结合

举个例子:通信线路
寻找一个费用,最少经过k条大于这个x的边
也就是大于这个费用的边权设为1,其他的为0,如果费用>k,则l=mid
1,0用双端队列(其实就是起到把短的放到前面,heap的意思)

/*在郊区有 N 座通信基站,P 条 双向 电缆,第 i 条电缆连接基站 Ai 和 Bi。

特别地,1 号基站是通信公司的总站,N 号基站位于一座农场中。

现在,农场主希望对通信线路进行升级,其中升级第 i 条电缆需要花费 Li。

电话公司正在举行优惠活动。

农产主可以指定一条从 1 号基站到 N 号基站的路径,并指定路径上不超过 K 条电缆,由电话公司免费提供升级服务。

农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。

求至少用多少钱可以完成升级。*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<deque>

using namespace std;

const int N = 1010, M = 20010;

int n, m, k;
int h[N], e[M], ne[M], idx,w[N];
deque<int>q;
int dist[N];
bool st[N];

void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

bool check(int bound)
{
	memset(st, 0, sizeof st);
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	q.push_back(1);

	while (q.size())
	{
		int t = q.front();
		q.pop_front();

		if (st[t])continue;
		st[t] = true;

		for (int i = h[t]; ~i; i = ne[i])
		{
			int j = e[i], v = w[i] > bound;
			if (dist[j] > dist[t] + v)
			{
				dist[j] = dist[t] + v;
				if (!v)q.push_front(j);
				else q.push_back(j);
			}
		}
	}
	return dist[n] <= k;
}

int main()
{
	cin >> n >> m >> k;

	memset(h, -1, sizeof h);
	while (m--)
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c), add(b, a, c);
	}
	int l = 0, r = 1e6 + 1;
	while (l < r)
	{
		int mid = l + r >> 1;
		if (check(mid))r = mid;
		else l = mid + 1;
	}

	if (r == 1e6 + 1)r = -1;
	cout << r <<endl;

	return 0;
}

与dp结合

举个例子:拯救大兵瑞恩

  1. 如何构建这样一张图?
    先用g[i][j]把二维的坐标映射到一维里面
    获取那些比较特殊的墙or门,存到edge中
    如果是可以走的,就add
    再枚举每个点,枚举每个方向,如果不存在edge中,则建立边add(a,b,0)

  2. 如何表示我的状态
    用二进制来表示我有钥匙的状态 state|=key

  3. dp
    如果有钥匙d[x,y,state]=d[x,y,state|key]
    向上下左右走,无门墙或者有钥匙
    d[a,b,state]=min(d[a,b,state],d[x,y,state]+1)

#include<cstring>
#include<iostream>
#include<algorithm>
#include<deque>
#include<set>

#define x first
#define y second

using namespace std;

typedef pair<int, int>PII;

const int N = 11, M = N * N, E = 400, P = 1 << 10;

int n, m, p, k;
int h[N], e[E], w[E], ne[E], idx;
int g[N][N], key[M];
int dist[M][P];
bool st[M][P];

set<PII>edges;
void build()
{
 int dx[4] = { -1,0,1,0 };
 int dy[4] = { 0,1,0,-1 };

 for(int i=1;i<=n;i++)
 	for(int j=1;j<=m;j++)
 		for (int u = 0; u < 4; u++)
 		{
 			int x = i + dx[u], y = j + dy[u];
 			if (!x || x > n || !y || y > m)continue;
 			int a = g[i][j], b = g[x][y];
 			if (edges.count({ a,b }) == 0)add(a, b, 0);
 		}
}

void add(int a, int b, int c)
{
 ne[idx] = h[a];
 e[idx] = b;
 w[idx] = c;
 h[a] = idx++;
}

int bfs()
{
 memset(dist, 0x3f, sizeof dist);
 dist[1][0] = 0;//坐标 状态

 deque<PII>q;
 q.push_back({ 1,0 });

 while (q.size())
 {
 	PII t = q.front();
 	q.pop_front();

 	if (st[t.x][t.y])continue;
 	st[t.x][t.y] = 1;

 	if (t.x == n * m)return dist[t.x][t.y];

 	if (key[t.x])
 	{
 		int state = t.y | key[t.x];
 		if (dist[t.x][state] > dist[t.x][t.y])
 		{
 			dist[t.x][state] = dist[t.x][t.y];
 			q.push_front({ t.x,state });
 		}
 	}

 	for (int i = h[t.x]; ~i; i = ne[i])
 	{
 		int j = e[i];
 		if (w[i] && !(t.y >> w[i] - 1 & 1))continue;
 		if (dist[j][t.y] > dist[t.x][t.y] + 1)
 		{
 			dist[j][t.y] = dist[t.x][t.y] + 1;
 			q.push_back({ j,t.y });
 		}
 	}

 }
 return -1;
}
int main()
{
 cin >> n >> m >> p >> k;

 for (int i = 1, t = 1; i <= n; i++)
 	for (int j = 1; j <= m; j++)
 		g[i][j] = t++;

 memset(h, -1, sizeof h);
 while (k--)
 {
 	int x1, y1, x2, y2, c;
 	cin >> x1 >> y1 >> x2 >> y2 >> c;
 	int a = g[x1][y1], b = g[x2][y2];
 	edges.insert({ a,b }), edges.insert({ b,a });
 	if (c)add(a, b, c), add(b, a, c);
 }
 build();

 int s;
 cin >> s;
 while (s--)
 {
 	int x, y, id;
 	cin >> x >> y >> id;
 	key[g[x][y]] |= 1 << id - 1;
 }

 cout << bfs() << endl;

 return 0;
}

最短路计数

伸缩的时候如果相同,则相加,如果大,则替代

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 100010, M = 400010, mod = 10003;

int n, m;
int h[N], e[M], ne[M], idx;
int dist[N], cnt[N];
int q[N];
void bfs()
{
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	cnt[1] = 1;

	int hh = 0, tt = 0;
	q[0] = 1;

	while (hh <= tt)
	{
		int t = q[hh++];
		for (int i = h[t]; ~i; i = ne[i])
		{
			int j = e[i];
			if (dist[j] > dist[t] + 1)
			{
				dist[j] = dist[t] + 1;
				cnt[j] = cnt[t];//缁ф壙
				q[++tt] = j;
			}
			else if (dist[j] == dist[t] + 1)
			{
				cnt[j] = (cnt[j] + cnt[t]) % mod;//澧炲姞
			}
		}
	}
}
void add(int a, int b)
{
	ne[idx] = h[a];
	e[idx] = b;

	h[a] = idx++;
}
int main()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);
	while (m--)
	{
		int a, b;
		cin >> a >> b;
		add(a, b), add(b, a);
	}
	bfs();
	for (int i = 1; i <= n; i++)cout << cnt[i];
	return 0;
}

Floyed

最短路

举个例子:牛的旅行

给牧区牧场添一条路径,两个牧区中各选一个牧区,用一条路径连接起来,使得联通的新牧区有最小直径

ans>=所有连通块1自己的最大直径
经过新边的最长路径

  • 用floyed求出任何两点之间的最短距离(联通的)
  • 求maxd[i]表示和i连通且和i最远的点的距离(内部最大值)
  • 枚举哪两个点的连边
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>

#define x first
#define y second

using namespace std;

typedef pair<int, int>PII;

const int N = 150;
const double INF = 1e20;

int n;
PII q[N];
char g[N][N];
double d[N][N], maxd[N];

double get_dist(PII a, PII b)
{
   double dx = a.x - b.x, dy = a.y - b.y;
   return sqrt(dx * dx + dy * dy);
}
int main()
{
   cin >> n;
   for (int i = 0; i < n; i++)cin >> q[i].x >> q[i].y;

   for (int i = 0; i < n; i++)cin >> g[i];

   for(int i=0;i<n;i++)
   	for(int j=0;j<n;j++)
   		if (i != j)
   		{
   			if (g[i][j] == '1')d[i][j] = get_dist(q[i], q[j]);
   			else d[i][j] = INF;
   		}
   for (int k = 0; k < n; k++)
   	for (int i = 0; i < n; i++)
   		for (int j = 0; j < n; j++)
   			d[i][j] = min(d[i][j], d[i][k] + d[k][j]);

   for (int i = 0; i < n; i++)
   	for (int j = 0; j < n; j++)
   		if (d[i][j] < INF)
   			maxd[i] = max(maxd[i], d[i][j]);
   double res1 = 0;
   for (int i = 0; i < n; i++)res1 = max(res1, maxd[i]);

   double res2 = INF;
   for (int i = 0; i < n; i++)
   	for (int j = 0; j < n; j++)
   		if (d[i][j] >= INF)
   			res2 = min(res2, get_dist(q[i], q[j]) + maxd[i] + maxd[j]);
   cout << max(res1, res2);

   return 0;
}

传递闭包

如果a和b关系确定,b和c确定,那么ac确定
if(d(i,k)&&d(k,j)) d(i,j)=1

举个例子:排序
如果d(i,i)=1矛盾
如果d(i,j),d(j,i)必有一个为1,则唯一确定
否则顺序不一定

/*给定 n 个变量和 m 个不等式。其中 n 小于等于 26,变量分别用前 n 的大写英文字母表示。

不等式之间具有传递性,即若 A>B 且 B>C,则 A>C。

请从前往后遍历每对关系,每次遍历时判断:

如果能够确定全部关系且无矛盾,则结束循环,输出确定的次序;
如果发生矛盾,则结束循环,输出有矛盾;
如果循环结束时没有发生上述两种情况,则输出无定解。*/
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 26;

int n, m;
bool g[N][N], d[N][N];
bool st[N];
void floyd()
{
	memcpy(d, g, sizeof d);

	for (int k = 0; k < n; k++)
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				d[i][j] |= d[i][k] && d[k][j];
	/*if(d(i,k)&&d(k,j)
	   d(i,j)=1*/
}
int check()
{
	for (int i = 0; i < n; i++)
		if (d[i][i])
			return 2;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < i; j++)
			if (!d[i][j] && !d[j][i])
				return 0;
	return 1;
}
int main()
{
	while (cin >> n >> m, n || m)
	{
		memset(g, 0, sizeof g);
		int type = 0, t;
		for (int i = 1; i <= m; i++)
		{
			char str[5];
			cin >> str;
			int a = str[0] - 'A', b = str[2] - 'A';

			if (!type)
			{
				g[a][b] = 1;
				floyd();
				type = check();
				if (type)t = i;
			}
		}
	}
}

求最小环

三重循环中,第一重k的循环后
d[i,j]表示从i-j只经过1-k-1的最短路径
所以i,j小于k的时候 i-k固定,k-j固定 进一步需要让j-i最小并且只经过1-k-1

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 110, INF = 0x3f3f3f3f;

int n, m;
int d[N][N], g[N][N];
int pos[N][N];
int path[N], cnt;

void get_path(int i, int j)
{
	if (pos[i][j] == 0)return;

	int k = pos[i][j];
	get_path(i, k);
	path[cnt++] = k;
	get_path(k, j);
}
int main()
{
	cin >> n >> m;
	memset(g, 0x3f, sizeof g);
	for (int i = 1; i <= n; i++)g[i][i] = 0;

	while (m--)
	{
		int a, b, c;
		cin >> a >> b >> c;
		g[a][b] = g[b][a] = min(g[a][b], c);
	}

	int res = INF;
	memcpy(d, g, sizeof d);

	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)d[i][j] + g[j][k] + g[k][i] < res)
				{
					res = d[i][j] + g[j][k] + g[k][i];
					cnt = 0;
					path[cnt++] = k;
					path[cnt++] = i;
					get_path(i, j);
					path[cnt++] = j;
				}
			}

		for(int i=1;i<=n;i++)
	       for(int j=1;j<=n;j++)
			   if (d[i][j] > d[i][k] + d[k][j])
			   {
				   d[i][j] = d[i][k] + d[k][j];
				   pos[i][j] = k;
			   }
	}

	if (res == INF)puts("no");
	else
	{
		for (int i = 0; i < cnt; i++)cout << path[i] << " ";
		cout << endl;
	}
	return 0;

}

与倍增的结合

牛站:很魔幻的一道题目,有一点点bellman的感觉,每次更新的是边,只更新一次,要用temp存起来,理解了很多遍没有理解清楚

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>

using namespace std;

const int N = 210;

int k, n, m, S, E;
int g[N][N];
int res[N][N];
void mul(int c[][N], int a[][N], int b[][N])
{
	static int temp[N][N];
	memset(temp, 0x3f, sizeof temp);
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				temp[i][j] = min(temp[i][j], a[i][k] + b[k][j]);
	memcpy(c, temp, sizeof temp);
}
void qmi()
{
	memset(res, 0x3f, sizeof res);
	for (int i = 1; i <= n; i++)res[i][i] = 0;

	while (k)
	{
		if (k & 1)mul(res, res, g);
		mul(g, g, g);
		k >>= 1;
	}
}
int main()
{
	cin >> k >> m >> S >> E;

	memset(g, 0x3f, sizeof g);
	map<int, int>ids;
	if (!ids.count(S))ids[S] = ++n;
	if (!ids.count(E))ids[E] = ++n;
	S = ids[E], E = ids[E];

	while (m--)
	{
		int a, b, c;
		cin >> c >> a >> b;
		if (!ids.count(a))ids[a] = ++n;
		if (!ids.count(b))ids[b] = ++n;
		a = ids[a], b = ids[b];

		g[a][b] = g[b][a] = min(g[a][b], c);
	}

	qmi();

	cout << res[S][E] << endl;
}

最小生成树

超级源点

自身的也变成有一条路径,一共有n+1个点

拓展成完全图

新边<=wi 不可能
新边>=wi+1
(sizesize-1)(wi-1)

/*给定一棵 N 个节点的树,要求增加若干条边,
把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。
求增加的边的权值总和最小是多少。*/

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 6010;

int n;
struct Edge
{
	int a, b, w;
	bool operator< (const Edge& t)const
	{
		return w < t.w;
	}
}e[N];
int p[N], SIZE[N];

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

int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		cin >> n;
		for (int i = 0; i < n - 1; i++)
		{
			int a, b, w;
			cin >> a >> b >> w;
			e[i] = { a,b,w };
		}

		sort(e, e + n - 1);

		for (int i = 1; i <= n; i++)p[i] = i, SIZE[i] = 1;

		int res = 0;
		for (int i = 0; i < n - 1; i++)
		{
			int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
			if (a != b)
			{
				res += (SIZE[a] * SIZE[b] - 1) * (w + 1);
				SIZE[b] += SIZE[a];
				p[a] = b;
			}
		}
		cout << res << endl;
	}
	return 0;
}

这是一开始都看成一个独立的点,然后由于是按照排序排好的,就是一个天然的prim,1*1-1=0不会增加权值

次小生成树

  • 方法a:先求最小生成树,然后枚举删除最小生成树中的边求解
  • 先求最小生成树,依次枚举非树边,将其加入,同时从树中去掉一条边,使其成为一棵树

具体步骤

  • 求最小生成树,统计标记每条边是树边还是非数边·,同时把最小生成树给建立出来
  • 预处理出来任意两点之间的边权最大值(环中,树上)
  • 枚举所有非权边 求sum+w-dist[a][b]
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N = 510, M = 10010;

int n, m;
struct Edge
{
	int a, b, w;
	bool f;
	bool operator< (const Edge& t)const
	{
		return w < t.w;
	}
}edge[M];

int p[N];
int dist[N][N];
int h[N], e[N * 2], w[N * 2], ne[N * 2], idx;

void add(int a, int b, int c)
{
	e[idx] = b;
	w[idx] = c;
	ne[idx] = h[a];
	h[a] = idx++;
}

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

void dfs(int u, int fa, int maxd, int d[])
{
	d[u] = maxd;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (j != fa)
		{
			dfs(j, u, max(maxd, w[i]), d);
		}
	}
}


int main()
{
	cin >> n >> m;;
	memset(h, -1, sizeof h);
	for (int i = 0; i < m; i++)
	{
		int a, b, w;
		cin >> a >> b >> w;
		edge[i] = { a,b,w };
	}
	sort(edge, edge + m);
	for (int i = 1; i <= n; i++)p[i] = i;

	LL sum = 0;
	for (int i = 0; i < m; i++)
	{
		int a = edge[i].a, b = edge[i].b, w = edge[i].w;
		int pa = find(a), pb = find(b);

		if (pa != pb)
		{
			p[pa] = pb;
			sum += w;
			add(a, b, w), add(b, a, w);
			edge[i].f = true;
		}
	}

	for (int i = 1; i <= n; i++)dfs(i, -1, 0, dist[i]);

	LL res = 1e18;
	for (int i = 0; i < m; i++)
	{
		if (!edge[i].f)
		{
			int a = edge[i].a, b = edge[i].b, w = edge[i].w;
			if (w > dist[a][b])
				res = min(res, sum + w - dist[a][b]);
		}
	}
	cout << res;
}

SPFA找负环

统计每个点入队次数 入队n次,存在负环
统计当前每个点最短路中包含的边数,边数>=n,存在负环
当所有点的入队次数>2n,则很大可能有负环

01分数规划

比值的最大最小,设置一个mid,如果>0,则是看是否存在正环,存在则满足,反之是负环
正环是最长路,dist要根据我们需要的东西来定
举个例子:观光奶牛

/*01分数规划*/

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 1010, M = 5010;
int n, m;
int wf[N];
int h[N], e[M], wt[M], ne[M], idx;
double dist[N];
int q[N], cnt[N];
bool st[N];

void add(int a, int b, int c)
{
	e[idx] = b, wt[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

bool check(double mid)
{
	memset(st, 0, sizeof st);
	memset(cnt, 0, sizeof cnt);

	int hh = 0, tt = 0;
	for (int i = 1; i <= n; i++)
	{
		q[tt++] = i;
		st[i] = true;
	}

	while (hh != tt)
	{
		int t = q[hh++];
		if (hh == N)hh = 0;
		st[t] = false;

		for (int i = h[t]; ~i; i = ne[i])
		{
			int j = e[i];
			if (dist[j] < dist[t] + wf[t] - mid * wt[i])
			{
				dist[j] = dist[t] + wf[t] - mid * wt[i];
				cnt[j] = cnt[t] + 1;
				if (cnt[j] >= n)return true;
				if (!st[j])
				{
					q[tt++] = j;
					if (tt == N)tt = 0;
					st[j] = true;
				}
			}
		}
	}
return false;
}
int main()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);

	for (int i = 1; i <= n; i++)cin >> wf[i];
	while (m--)
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
	}
	double l = 0, r = 1010;
	while (r - l > 1e-4)
	{
		double mid = (l + r) / 2;
		if (check(mid))l = mid;
		else r = mid;
	}

	cout << r;
	return 0;

}

约数差分

c在的地方是起点
求最小值,走最长路,求最大值,走最短路
A=B A>=B A<=B
A=A+1
X>=1 X>=x0+1 x0=0

TIP:如果能确定是存在负环的话,可以用栈(深搜)进行spfa,这样子能更快的找到负环然后返回
举个例子:糖果

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

typedef long long LL;

const int N = 100010, M = 300010;

int n, m;
int h[N], e[M], ne[N], w[M], idx;

LL dist[N];
int q[N], cnt[N];
bool st[N];
bool spfa()
{
	int hh = 0, tt = 1;
	memset(dist, -0x3f, sizeof dist);
	dist[0] = 0;
	q[0] = 0;
	st[0] = true;

	while (hh != tt)
	{
		int t = q[--tt];//用栈
		st[t] = false;

		for (int i = h[t]; ~i; i = ne[i])
		{
			int j = e[i];
			if (dist[j] < dist[t] + w[i])//最长路
			{
				dist[j] = dist[t] + w[i];
				cnt[j] = cnt[t] + 1;
				if (cnt[j] >= n + 1)return false;
				if (!st[j])
				{
					q[tt++] = j;
					st[j] = true;
				}
			}
		}

	}
	return true;
}
void add(int a, int b, int c)
{
	e[idx] = b;
	ne[idx] = h[a];
	w[idx] = c;
	h[a] = idx++;
}
int main()
{
	cin >> m >> n;
	memset(h, -1, sizeof h);
	while (m--)
	{
		int x, a, b;
		cin >> x >> a>> b;

		if (x == 1)add(b, a, 0), add(a, b, 0);
		else if (x == 2)add(a, b, 1);
		else if (x == 3)add(b, a, 0);
		else if (x == 4)add(b, a, 1);
		else add(a, b, 0);
	}
	for (int i = 1; i <= n; i++)add(0, i, 1);

	if (!spfa())puts("-1");
	else
	{
		LL res = 0;
		for (int i = 1; i <= n; i++)res += dist[i];
		cout << res;
	}
	return 0;
}

在线求LCA

倍增 fa[i][j]表示从i开始,向上走2^j能够到达的节点
步骤:

  • 先让两点跳到同一个高度
  • 让他们继续向上跳,直到到达公共的下一层
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;

#include<queue>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e6 + 10;
int e[N], ne[N], depth[N], h[N],q[N];
int fa[N][16];
int idx = 0;
int n;
void bfs(int root)
{
   memset(depth, 0x3f, sizeof depth);
   depth[root] = 1;
   depth[0] = 0;
   int hh = 0, tt = 0;

   q[0] = root;
   while (hh <= tt)
   {
   	int t = q[hh++];
   	for (int i = h[t]; ~i; i = ne[i])
   	{
   		int j = e[i];
   		if (depth[j] > depth[t] + 1)
   		{
   			depth[j] = depth[t] + 1;
   			q[++tt] = j;
   			fa[j][0] = t;
   			for (int k = 1; k<= 15; k++)
   				fa[j][k] = fa[fa[j][k - 1]][k-1];
   		}
   	}
   }

}

int lca(int a, int b)
{
   if (depth[a] < depth[b])swap(a, b);
   for (int k = 15; k >= 0; k--)
   	if (depth[fa[a][k]] >= depth[b])
   		a = fa[a][k];
   if (a == b)return a;
   for (int k = 15; k >= 0; k--)
   	if (fa[a][k] != fa[b][k])
   	{
          a = fa[a][k];
   	   b = fa[b][k];
   	}
   return fa[a][0];
}
void add(int a, int b)
{
   ne[idx] = h[a];
   e[idx] = b;
   h[a] = idx++;
}
int main()
{
   cin >> n;
   int root = 0;
   memset(h, -1, sizeof h);

   for (int i = 0; i < n; i++)
   {
   	int a, b;
   	cin >> a >> b;
   	if (b == -1)root = a;
   	else add(a, b), add(b, a);
   }

   bfs(root);
   int m; 
   cin >> m;
   while (m--)
   {
   	int a, b;
   	cin >> a >> b;
   	int p = lca(a, b);
   	if (p == a)puts("1");
   	else if (p == b)puts("2");
   	else puts("0");
   }
   return 0;
}

离线求LCA(TARJAN)

深度优先遍历的时候,将所有的点分为三大类:已经遍历过且回溯过的点,正在搜索的分支,还没有搜索到的点

举个例子:给出n个点的一张图,多次询问两个点之间的距离:d(x)+d(y)-2*d§
dfs设置深度
tarjan:把这个点状态设为1(表示正在走的分支),进行深搜,把所有子结点都进入tarjan,并且把j和u合并,这样之后可以询问了,如果询问的点已经被搜过了,那么就可以寻找其父亲节点(父亲节点必然也是在这个分支上的,大不了就是最上面的根节点)

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>

using namespace std;

typedef pair<int, int>PII;
const int N = 20010, M = N * 2;

int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
int p[N];
int res[N];
int st[N];
vector<PII> query[N];
void add(int a, int b, int c)
{
	e[idx] = b;
	w[idx] = c;
	ne[idx] = h[a];
}

void dfs(int u, int fa)
{
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (j == fa)continue;
		dist[j] = dist[u] + w[i];
		dfs(j, u);
	}
}

int find(int x)
{
	if (p[x] != x)p[x] = find(p[x]);
	return p[x];
}
void tarjan(int u)
{
	st[u] = 1;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!st[j])
		{
			tarjan(j);
			p[j] = u;
		}
	}

	for (auto item : query[u])
	{
		int y = item.first, id = item.second;
		if (st[y] == 2)
		{
			int anc = find(y);
			res[id] = dist[u] + dist[y] - dist[anc] * 2;
		}
	}
	st[u] = 2;
}
int main()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);
	for (int i = 0; i < n - 1; i++)
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c), add(b, a, c);
	}
	
	for (int i = 0; i < m; i++)
	{
		int a, b;
		cin >> a >> b;
		if (a != b)
		{
			query[a].push_back({ b,i });
			query[b].push_back({ a,i });
		}
	}
	for (int i = 1; i <= n; i++)p[i] = i;

	dfs(1, -1);
	tarjan(1);

	for (int i = 0; i < m; i++)cout << res[i];

	return 0;

}

有向图的强连通分量DFS

对于一个有向图连通分量,连通分量里任意两点u,v,可以互相走到
有向图经过缩点可以编成有向无环图

-如何判断是不是在强连通分量中

  • 存在后向边指向祖先节点
  • 先走横插边,再走到祖先

tarjan的实现:

  • 首先设置时间戳
  • 加入栈,并且把在栈设置为正确
  • 深搜它的子结点,如果它还没有过,则对它进行处理,这个父亲的low进行更新,如果在里面了,则进行更新
  • 询问这个是不是我要找的割点,如果是的话,把连通块的数量增加1,对于所有的y,y是栈内所有的,设置为不在站内,并把id设置为scc_cnt,这个连通块size+1,直到到达了y==u
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 10010, M = 50010;

int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, SIZE[N];
int dout[N];
void add(int a, int b)
{
   e[idx] = b;
   ne[idx] = h[a];
   h[a] = idx++;
}

void tarjan(int u)
{
   dfn[u] = low[n] = ++timestamp;
   stk[++top] = u, in_stk[u] = true;
   for (int i = h[u]; i != -1; i = ne[i])
   {
   	int j = e[i];
   	if (!dfn[j])
   	{
   		tarjan(j);
   		low[u] = min(low[u], low[j]);
   	}
   	else if (in_stk[j])low[u] = min(low[u], dfn[j]);
   }
   if (dfn[u] == low[u])
   {
   	++scc_cnt;
   	int y;
   	do
   	{
   		y = stk[top--];
   		in_stk[y] = false;
   		id[y] = scc_cnt;
   		SIZE[scc_cnt]++;
   	} while (y != u);
   }


}
int main()
{
   cin >> n >> m;
   memset(h, -1, sizeof h);
   while (m--)
   {
   	int a, b;
   	cin >> a >> b;
   	add(a, b);
   }

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

   for(int i=1;i<=n;i++)
   	for (int j = h[i]; ~j; j = ne[j])
   	{
   		int k = e[j];
   		int a = id[i], b = id[k];
   		if (a != b)dout[a]++;
   	}

   int zeros = 0, sum = 0;
   for(int i=1;i<=scc_cnt;i++)
   	if (!dout[i])
   	{
   		zeros++;
   		sum += SIZE[i];
   		if (zeros > 1)
   		{
   			sum = 0;
   			break;
   		}
   	}
   cout << sum << endl;

}

使其成为强连通分量

  • 对于有向图,起点P个,终点Q个,还需要max(p,q)
  • 对于无向图 cnt+1/2
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 110, M = 10010;

int n;
int h[N], ne[M], e[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt;
int din[N], dout[N];

void add(int a, int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}


void tarjan(int u)
{
	dfn[u] = low[u] = ++timestamp;
	stk[++top] = u, in_stk[u] = true;

	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!dfn[j])
		{
			tarjan(j);
			low[u] = min(low[u], low[j]);
		}
		else if (in_stk[j])
		{
			low[u] = min(low[u], dfn[j]);
		}
	}

	if (dfn[u] == low[u])
	{
		++scc_cnt;
		int y;
		do
		{
			y = stk[top--];
			in_stk[y] = false;
			id[y] = scc_cnt;
		} while (y != u);
	}

}
int main()
{
	cin >> n;
	memset(h, -1, sizeof h);
	for (int i = 1; i <= n; i++)
	{
		int t;
		while (cin >> t, t)add(i, t);
	}

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

	for(int i=1;i<=n;i++)
		for (int j = h[i]; j != -1; j = ne[i])
		{
			int k = e[j];
			int a = id[i], b = id[k];
			if (a != b)
			{
				dout[a]++;
				din[b]++;
			}
		}

	int a = 0, b = 0;
	for (int i = 1; i <= scc_cnt; i++)
	{
		if (!din[i])a++;
		if (!dout[i])b++;
	}
	cout << a;
	if (scc_cnt == 1)puts("0");
	else cout << max(a, b);
}

强连通图解决差束约分问题

就是看是否存在环
tarjan +缩点建图+拓扑排序
原理:
首先用tarjan求scc
一个正环一定是某一个scc当中的,对于一个scc中的所有边,
只要一个边的权重是严格>0
如 u + w → v,w>0
又u和v 在一个scc中,则v也一定能到u(且w[v][u]>=0(因为我们的不等式约束得到的))
即只要scc中有一个边 >=0 就必然存在正环
则 scc中无正环 <=> scc中的 边==0 <=> scc中所有点相同 (由不等式知双向边0时 A=B<=> 可近似看成一个点
那么当没有正环时,经过tarjan后的图就是topo图
x[i]最小 <=> 求最长路dist[i]

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010, M = 600010;

int n, m;
int h[N], hs[N], e[M], ne[M], w[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, scc_size[N];
int dist[N];

void add(int h[], int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

void tarjan(int u)
{
    dfn[u] = low[u] = timestamp++;
    stk[++top] = u, in_stk[u] = true;//stk[++top]要和stk[top--]配 而不是stk[top++] stk[0++] = u stk[1++] = t 

    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (!dfn[j])
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if (in_stk[j]) low[u] = min(low[u], dfn[j]);
    }
    if (dfn[u] == low[u])
    {
        scc_cnt++;
        int y;
        do
        {
            y = stk[top--];//忘了强连通分量都是通过dfs后在一个栈里的
            in_stk[y] = false;//漏了
            id[y] = scc_cnt;
            scc_size[scc_cnt]++;
        } while (y != u);//漏了
    }
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    memset(hs, -1, sizeof h);
    // 超级源点 和i有一个1的边
    for (int i = 1; i <= n; i++)add(h, 0, i, 1);
    while (m--)
    {
        int t, a, b;
        cin >> t >> a >> b;
        if (t == 1)add(h, b, a, 0), add(h, a, b, 0);
        else if (t == 2)add(h, a, b, 1);
        else if (t == 3)add(h, b, a, 0);
        else if (t == 4)add(h, b, a, 1);
        else add(h, a, b, 0);
    }
    tarjan(0);
    bool success = true;
    for (int i = 0; i <= n; i++)
    {
        for (int j = h[i]; ~j; j = ne[j])
        {
            int k = e[j];
            int a = id[i], b = id[k];
            // 如果a和b在一个scc里,判断w[a][b]是否>0
            if (a == b)
            {
                if (w[j] > 0)
                {
                    success = false;
                    break;
                }
            }
            // 如果不在一个scc里 在新图里加一条边
            else add(hs, a, b, w[j]);
        }
        if (!success) break;
    }
    if (!success) cout << "-1";
    else
    {
        // 有解 求最长路
        for (int i = scc_cnt; i; i--)
        {
            for (int j = hs[i]; ~j; j = ne[j])
            {
                int k = e[j];
                dist[k] = max(dist[k], dist[i] + w[j]);
            }
        }
        LL res = 0;
        // 结果 = 新图里每个scc的距离 * scc里的点数 = dist[scc] * cnt[scc] 
        for (int i = 1; i <= scc_cnt; i++) res += (LL)dist[i] * scc_size[i];
        cout << res;
    }
    return 0;
}

二分图

棋盘是天然的二分,所以查找的时候只要找x+y是奇数或是偶数的

染色法

举个例子:关押罪犯
看能否把大于x的哪些人二分到两边
dfs(当前是什么,眼色,mid)

bool dfs(int u, int c, int mid)
{
	color[u] = c;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (w[i] <= mid)continue;
		if (color[j])
		{
			if (color[j] == c)return false;
		}
		else if (!dfs(j,3-c,mid))return false;
	}
	return true;
}

bool check(int mid)
{
	memset(color, 0, sizeof color);
	for (int i = 1; i <= n; i++)
		if (color[i] == 0)
			if (!dfs(i, 1, mid))return false;
	return true;
}

int main()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);

	while (m--)
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c), add(b, a, c);
	}
	int l = 0, r = 1e9;
	while (l < r)
	{
		int mid = l + r >> 1;
		if (check(mid))r = mid;
		else l = mid + 1;
	}

	cout << r << endl;
	return 0;
}

匈牙利算法

最大匹配数=最小点覆盖=总点数-最大独立集=总点数-最小路径覆盖

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 110;
int n, m, k;
bool g[N][N], st[N];
int match[N];

bool find(int x)
{
	for(int i=1;i<m;i++)
		if (!st[i] && g[x][i])
		{
			st[i] = true;
			int t = match[i];
			if (t == 0 || find(t))
			{
				match[i] = x;
				return true;
			}
		}
	return false;
}

int main()
{
	while (cin >> n, n)
	{
		cin >> m >> k;
		memset(g, 0, sizeof g);
		memset(match, 0, sizeof match);

		while (k--)
		{
			int t, a, b;
			cin >> t >> a >> b;
			if (!a || !b)continue;
			g[a][b] = true;
		}

		int res = 0;
		for (int i = 1; i < n; i++)
		{
			memset(st, 0, sizeof st);
			if (find(i))res++;
		}

		cout << res;

	}
}

最大独立集

选出最多点,使得选出来的点之间没有边
二分图中,求最大独立集
《=》去掉最少的点,将所有边破坏
《=》找最小点覆盖
《=》找最大匹配
举个例子:骑士
选点:选的点两两之间没有边
n*m-k-最大匹配数

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

#define x first
#define y second

typedef pair<int, int>PII;

const int N = 110;

int n, m, k;
PII match[N][N];
bool g[N][N], st[N][N];

int dx[] = { -2,-1,1,2,2,1,-1,-2 };
int dy[] = { 1,2,2,1,-1,-2,-2,-1 };
bool find(int x, int y)
{
    for (int i = 0; i < 8; i++)
    {
        int a = x + dx[i], b = y + dy[i];
        if (a<1 || a>n || b<1 || b>m)continue;
        if (g[a][b] || st[a][b])continue;
        st[a][b] = true;

        PII t = match[a][b];
        if (t.x == 0 || find(t.x, t.y));
        {
            match[a][b] = { x,y };
            return true;
        }
    }
    return false;
}
int main()
{
    cin >> n >> m >> k;
    for (int i = 0; i < k; i++)
    {
        int x, y;
        cin >> x >> y;
        g[x][y] = true;
    }

    int res = 0;
    for(int i=1;i<=n;i++)
        for (int j = 1; j <= m; j++)
        {
            if ((i + j) % 2 || g[i][j])continue;
            memset(st, 0, sizeof st);
            if (find(i, j))res++;
        }
    cout << n * m - k - res;
}
}

欧拉回路

起点和终点:奇书
中间点:偶数
对于有向图:

  • 所有边连通,所有点的出度等于入度
  • 除两点外,所有点的出度等于入度,剩余两点满足,一个出比入多一,一个入比出多一

对于无向图:

  • 度数为奇数的点只有0/2个

定一张图,请你找出欧拉回路,
即在图中找一个环使得每条边都在环上出现恰好一次。
输入格式
第一行包含一个整数 t,t∈{1,2},
如果 t=1,表示所给图为无向图,如果 t=2,表示所给图为有向图。
第二行包含两个整数 n,m,表示图的结点数和边数。
接下来 m 行中,第 i 行两个整数 vi,ui,
表示第 i 条边(从 1 开始编号)。
如果 t=1 则表示 vi 到 ui 有一条无向边。
如果 t=2 则表示 vi 到 ui 有一条有向边。
图中可能有重边也可能有自环。点的编号从 1 到 n

统计出度入度,进行第一次判断
在进行dfs,通过边数判断能否联通
dfs,利用栈搜索先到达终点,记录已经走过的点,在回溯的时候走完没有走完的点
for 点边,标记,深搜儿子结点

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 100010, M = 400010;

int type;
int n, m;
int h[N], e[M], ne[M], idx;
bool used[M];
int ans[M / 2], cnt;
int din[N], dout[N];

void add(int a, int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}

void dfs(int u)
{
	for (int i = h[u]; ~i; i = ne[i])
	{
		if (used[i])continue;
		used[i] = true;
		if (type == 1)used[i ^ 1] = true;
		dfs(e[i]);

		if (type == 1)
		{
			int t = i / 2 + 1;
			if (i & 1)t = -t;
			ans[++cnt] = t;
		}
		else ans[++cnt] = i + 1;
	}
}
int main()
{
	cin >> type >> n >> m;
	memset(h, -1, sizeof h);

	for (int i = 0; i < m; i++)
	{
		int a, b;
		cin >> a >> b;
		add(a, b);

		if (type == 1)add(b, a);
		din[b]++, dout[a]++;
	}
	if (type == 1)
	{
		for(int i=1;i<=n;i++)
			if (din[i] + dout[i] & 1)
			{
				puts("NO");
				return 0;
			}
	}
	else
	{
		for(int i=1;i<=n;i++)
			if (din[i] != dout[i])
			{
				puts("no");
				return 0;
			}
	}

	for(int i=1;i<=n;i++)
		if (h[i] != -1)
		{
			dfs(i);
			break;
		}

	if (cnt < m)
	{
		puts("no");
		return 0;
	}
	puts("yes");
	for (int i = cnt; i; i--)cout << ans[i];
}

TIP
如果想要字典序最小,对于每个父亲,有着很多儿子,用vector存,并对其进行排序,从小到大,再深搜

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<stack>
#include<vector>
using namespace std;
const int MAX = 100010;
int n, m, u, v, del[MAX];
int du[MAX][2];
stack <int> st;
vector<int>G[MAX];
void dfs(int now)
{
	for (int i = del[now]; i < G[now].size(); i = del[i])
	{
		del[now] = i + 1;
		dfs(G[now][i]);
	}
	st.push(now);
}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		cin >> u >> v;
		G[u].push_back(v);
		du[u][1]++;
		du[v][0]++;
	}
	for (int i = 1; i <= n; i++)
	{
		sort(G[i].begin(), G[i].end());
	}
	int S = 1,cnt[2] = { 0,0 };
	bool flag = 1;
	for (int i = 1; i <= n; i++)
	{
		if (du[i][1] != du[i][0]) flag = 0;
		if (du[i][1] - du[i][0] == 1) cnt[1]++, S = i;
		if (du[i][0] - du[i][1] == 1) cnt[0]++;
	}
	if ((!flag) && !(cnt[0] == cnt[1] && cnt[0] == 1))
	{
		printf("No");
		return 0;
	}
	dfs(S);
	while (!st.empty())
	{
		cout << st.top();
		st.pop();
	}

}

拓扑排序

约束差分求最长路

  • 边权没有限制 spfa mn
  • 边权非负 tarjan n+m
  • 边权为正 拓扑 n+m

一条单向的铁路线上,依次有编号为 1, 2, …, n的 n个火车站。每个火车站都有一个级别,最低为 1级。
现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x
,则始发站、终点站之间所有级别大于等于火车站 x 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点) 
第一行包含 2个正整数 n,m,用一个空格隔开。
第 i+1 行(1≤i≤m)中,首先是一个正整数 2≤si≤n),表示第 i趟车次有 si个停靠站;接下来有 si个正整数,表示所有停靠站的编号,从小到大排列。

题目给出了停靠站的编号,可以得到起点终点,以及没有停靠点的编号,由题意可以知道,停靠点的等级>=未停靠点等级+1,由此可以进行连边
观察数据,最坏情况1000趟火车,每趟500个点停,500不停,不停的站,向停的连边,5005001000=2.5*10^8,spfa可能超时
由于本题中的所有点的权值都是大于0,并且一定满足要求=>所有车站都等级森严=>
不存在环=>=>可以拓扑排序得到拓扑图使用递推求解差分约束问题
思路是:通过拓扑排序得到拓扑图,跑最长路
tip:如果暴力建图需要mn,可能超时,可以在中间连一个虚拟节点,其向左连0,向右连1,因此需要(m+n)条边,n+m个点

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>

using namespace std;

const int N = 2007, M = 5000007;

int n, m;
int ver[M], nex[M], edge[M], head[N], tot;
int din[N];
int vis[N];
int q[N];
int dist[N];

void add(int x, int y, int z) {
    ver[tot] = y;
    edge[tot] = z;
    nex[tot] = head[x];
    head[x] = tot++;
    din[y] ++;
}

void toposort() {
    int hh = 0, tt = -1;
    for (int i = 1; i <= n + m; ++i) {//一共n + m个点,要遍历所有的点
        if (!din[i]) {
            q[++tt] = i;
            if (tt == N)tt = 0;
        }
    }

    while (hh <= tt) {

        int x = q[hh++];
        if (hh == N)hh = 0;

        for (int i = head[x]; ~i; i = nex[i]) {
            int y = ver[i];
            if (--din[y] == 0) {
                q[++tt] = y;
                if (tt == N)tt = 0;
            }
        }
    }
}

int main() {
    scanf("%d%d", &n, &m);
    memset(head, -1, sizeof head);

    for (int i = 1; i <= m; ++i) {
        memset(vis, 0, sizeof vis);
        int t, stop;
        scanf("%d", &t);
        int start = n, end = 1;
        while (t--) {
            scanf("%d", &stop);//输入的是编号
            start = min(start, stop);
            end = max(end, stop);
            vis[stop] = true;//代表该站要停靠.
        }
        int source = n + i;//n + 1,虚拟点 
        for (int j = start; j <= end; ++j) {//该线路上的所有经过的站点的编号一定在始发站和终点站之间
            if (vis[j])//要停靠,说明是右部点
                add(source, j, 1);
            else add(j, source, 0);//左部点 
        }
    }

    toposort();

    for (int i = 1; i <= n; ++i)//A ≥ 1
        dist[i] = 1;
    for (int i = 0; i < n + m; ++i) {//手写的队列是从0开始的
        int x = q[i];
        for (int j = head[x]; ~j; j = nex[j]) {
            int y = ver[j], z = edge[j];
            dist[y] = max(dist[y], dist[x] + z);
        }
    }
    int res = 0;
    for (int i = 1; i <= n; ++i)//满足所有约束条件的解中最大值既是题目要求的最高的级别
        res = max(res, dist[i]);

    printf("%d\n", res);
    return 0;

一般用法

按照先后顺序输出,树变成线性
举个例子:家谱树
先把所有出度为0的进入队伍,寻找儿子,并且将儿子的出度-1,看是否有0的

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

const int N = 110, M = N * N / 2;

int n;
int h[N], ne[N], e[M], idx;
int q[N];
int d[N];

void add(int a, int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}
void topsort()
{
	int hh = 0, tt = -1;
	for (int i = 1; i <= n; i++)
		if (!d[i])
			q[++tt] = i;

	while (hh <= tt)
	{
		int t = q[hh++];
		for (int i = h[t]; ~i; i = ne[i])
		{
			int j = e[i];
			if (--d[j] == 0)
				q[++tt] = j;
		}
	}
}
	
	
	
int main()
{
	cin >> n;
	memset(h, -1, sizeof h);

	for (int i = 1; i <= n; i++)
	{
		int son;
		while (cin >> son, son)
		{
			add(i, son);
			d[son]++;
		}
	}
	topsort();

	for (int i = 0; i < n; i++)cout << q[i];
	return 0;
}

树状数组

兼备快速求前缀和,快速修改某个数的功能
lowbit操作找出最低位的1
add 从这个数向大循环,循环到的+c
sum 从这个数向小循环,res+tr[i]

举个例子:楼兰图腾
寻找数列里面,左边大于(小于)他的,右边大于(小于)它的
每次一个数字,在他的树状数组里就是在它对应的大小的地方+1
先从小到大循环,可以找到每个数左边大于他小于他的个数,也就是寻找树状数组里面已经存在的(已经存在:这些数字是在这个数列里面,是在它的左边)在他右边的数字的个数(这个说明比它大)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N = 200010;

int n;
int a[N];
int tr[N];
int Greater[N], lower[N];

int lowbit(int x)
{
	return x & -x;
}

void add(int x, int c)
{
	for (int i = x; i <= n; i += lowbit(i))tr[i] += c;
}
int sum(int x)
{
	int res = 0;
	for (int i = x; i; i -= lowbit(i))res += tr[i];
	return res;

}
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];

	for (int i = 1; i <= n; i++)
	{
		int y = a[i];
		Greater[i] = sum(n) - sum(y);
		lower[i] = sum(y - 1);
		add(y, 1);
	}

	memset(tr, 0, sizeof tr);
	LL res1 = 0, res2 = 0;
	for (int i = n; i; i--)
	{
		int y = a[i];
		res1 += Greater[i] * (LL)(sum(n) - sum(y));
		res2 += lower[i] * (LL)(sum(y - 1));
		add(y, 1);
	}

	cout << res1 << res2;
	return 0;
}

举个例子:谜一样的牛

有 n头奶牛,已知它们的身高为 1∼n
且各不相同,但不知道每头奶牛的具体身高。
现在这 n 头奶牛站成一列,已知第 i头牛前面有 Ai 头牛比它低,求每头奶牛的身高。

从后往前去考虑,因为最后一头牛,统计了在它之前的所有的牛,因此加入他比x头牛高,则必然高度是x+1
对于倒数第二头牛来说,是在除去了上述x+1的区间内,选取An-1 +1小的数字
一开始初始化树状数组是说,每个都是1的情况下,t[i]=lowbit[i],因为(l,r]的长度就是r的二进制表示的最后一个1的对应次幂,包含2^i1个数字,也就是x的二进制表示的最后一个1

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 100010;

int n;
int h[N];
int ans[N];
int tr[N];

int lowbit(int x)
{
	return x & -x;
}

void add(int x, int c)
{
	for (int i = x; i <= n; i += lowbit(i))tr[i] += c;

}
int sum(int x)
{
	int res = 0;
	for (int i = x; i; i -= lowbit(i))res += tr[i];
	return res;
}
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> h[i];

	for (int i= 1; i <= n; i++)
		tr[i] = lowbit(i);

	for (int i = n; i; i--)
	{
		int k = h[i] + 1;
		int l = 1, r = n;
		while (l < r)
		{
			int mid = l + r >> 1;
			if (sum(mid) >= k)r = mid;
			else l = mid + 1;
		}
		ans[i] = r;
		add(r, -1);
	}
	for (int i = 1; i <= n; i++)cout << ans[i];
}

线段树

动态修改某个数,动态查找某个数
要开4N的空间
pushup:结合题目来把某些数值从下向上传递
build:用递归去建立,直到l==r
query:查询,如果查询区间把某个区间全覆盖了,就返回这个区间,否则继续裂开
modify:直到某个区间的左右和我要的左右完全相同,否则不断裂开,裂完记得pushup

给两种操作。
添加操作:向序列后添加一个数,序列长度变成 n+1;
询问操作:询问这个序列中最后 L 个数中最大的数是多少

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
const int N = 1010;
int n, m,p;
struct Node
{
	int l, r;
	int v;
}tr[N*4];

void pushup(int u)
{
	tr[u].v = max(tr[u << 1].v, tr[u << 1 | 1].v);
}
void build(int u, int l, int r)
{
	tr[u] = { l,r };
	if (l == r)return;
	int mid = l + r >> 1;
	build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}

int query(int u, int l, int r)
{
	if (tr[u].l >= l && tr[u].r <= r)return tr[u].v;

	int mid = tr[u].l + tr[u].r >> 1;
	int v = 0;
	if (l <= mid)v = query(u << 1, l, r);
	if (r > mid)v = max(v, query(u << 1 | 1, l, r));

	return v;
}

void modify(int u, int x, int v)
{
	if (tr[u].l == x && tr[u].r == x)tr[u].v = v;
	else
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if (x <= mid)modify(u << 1, x, v);
		else modify(u << 1 | 1, x, v);
		pushup(u);
	}
}


int main()
{
	int n = 0, last = 0;
	cin >> m >> p;
	build(1, 1, m);

	int x;
	char op[2];
	while (m--)
	{
		cin >> op >> x;
		if (*op == 'Q')
		{
			last = query(1, n - x + 1, n);
			cout << last;
		}
		else
		{
			modify(1, n + 1, (last + x) % p);
			n++;
		}
	}
}

举个例子:区间最大公约数

C lrd 把A[l]…A[r]都加上d
Q lr 询问A[l]…A[r]的最大公约数

gcd(a,b)=gcd(a,a-b)
gcd(a,b,c)=gcd(gcd(a,b),gcd(b,c)) =gcd(a,b-a,c-a)
gcd{a1,a2,…,an}=gcd{a1,a2−a1,…,an−an−1}
gcd{a1,a2,…an}=gcd{a1,a2-a1,…,an-an-1}

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long ll;
const int N = 500010;

int n, m;
ll w[N];
struct Node
{
	int l, r;
	ll sum, d;
}tr[N*4];

ll gcd(ll a, ll b)
{
	return b ? gcd(b, a % b) : a;
}

void pushup(Node& u, Node& l, Node& r)
{
	u.sum = l.sum + r.sum;
	u.d = gcd(l.d, r.d);
}

void pushup(int u)
{
	pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
	if (l == r)
	{
		ll b = w[r] - w[r - 1];
		tr[u] = { l,r,b,b };
	}
	else
	{
		tr[u].l = l, tr[u].r = r;
		int mid = l + r >> 1;
		build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
		pushup(u);
	}
}

void modify(int u, int x, ll v)
{
	if (tr[u].l == x && tr[u].r == x)
	{
		ll b = tr[u].sum + v;
		tr[u] = { x,x,b,b };
	}
	else
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if (x <= mid)modify(u << 1, x, v);
		else modify(u << 1 | 1, x, v);
		pushup(u);
	}
}

Node query(int u, int l, int r)
{
	if (tr[u].l >= l && tr[u].r <= r)return tr[u];
	else
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if (r <= mid)return query(u << 1, l, r);
		else if (l > mid)return query(u << 1 | 1, l, r);
		else
		{
			auto left = query(u << 1, l, r);
			auto right = query(u << 1 | 1, l, r);
			Node res;
			pushup(res, left, right);
			return res;
		}
	}
}

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> w[i];
	build(1, 1, n);

	int l, r;
	ll d;
	char op[2];
	while (m--)
	{
		cin >> op >> l >> r;
		if (*op == 'Q')
		{
			auto left = query(1, 1, l), right = query(1, l + 1, r);
			cout << abs(gcd(left.sum, right.d));
		}
		else
		{
			cin >> d;
			modify(1, l, d);
			if (r + 1 <= n)modify(1, r + 1, -d);
		}
	}
}

举个例子:一个简单的整数问题

C lrd 表示把A[l]…A[r]都加上d
C lr 表示询问数列中l-r个数字的和

pushdown:懒标记,用于每一次操作之前,把之前的操作落实到叶结点,以便于其他操作的从后向前的正确性,用于修改操作的时候,减少全部直接到最小区间(modify和query都需要)
线段树维护sum,add,都要在pushdown中进行修改
query:

  • (边界条件)如今区间被要查询的全覆盖,则返回sum
  • 懒标记下移
  • 讨论mid和l,r的关系,进行进一步sum的累加

modify:

  • 如果全覆盖,改变sum,区间长度*d,改变add,add+d
  • 将懒标记下移,向下递归,pushup

pushdown:

  • 改变左右儿子的sum,add,并且把自己的标记清0

pushup:

  • 修改之后返回sum
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
typedef long long ll;
const int N = 100010;

int n, m;
int w[N];
struct Node
{
	int l, r;
	ll sum, add;
}tr[N*4];

void pushup(int u)
{
	tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void pushdown(int u)
{
	auto& root = tr[u], & left = tr[u << 1], & right = tr[u << 1 | 1];
	if (root.add)
	{
		left.add += root.add, left.sum += (ll)(left.r - left.l + 1) * root.add;
		right.add += root.add, right.sum += (ll)(right.r - right.l + 1) * root.add;
		root.add = 0;
	}
}

void build(int u, int l, int r)
{
	if (l == r)tr[u] = { l,r,w[r],0 };
	else
	{
		tr[u] = { l,r };
		int mid = l + r >> 1;
		build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
		pushup(u);
	}
}

void modify(int u, int l, int r, int d)
{
	if (tr[u].l >= l && tr[u].r <= r)
	{
		tr[u].sum += (ll)(tr[u].r - tr[u].l + 1) * d;
		tr[u].add += d;
	}
	else
	{
		pushdown(u);
		int mid = tr[u].l + tr[u].r >> 1;
		if (l <= mid)modify(u << 1, l, r, d);
		if (r > mid)modify(u << 1 | 1, l, r, d);
		pushup(u);
	}
}

ll query(int u, int l, int r)
{
	if (tr[u].l >= l && tr[u].r <= r)return tr[u].sum;

	pushdown(u);
	int mid = tr[u].l + tr[u].r >> 1;
	ll sum = 0;
	if (l <= mid)sum = query(u << 1, l, r);
	if (r > mid)sum += query(u << 1 | 1, l, r);
	return sum;

}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> w[i];

	build(1, 1, n);

	char op[2];
	int l, r, d;

	while (m--)
	{
		cin >> op >> l >> r;
		if (*op == 'C')
		{
			cin >> d;
			modify(1, l, r, d);
		}
		else
			cout << query(1, l, r);
	}
}

可持久化

前提:本身就是拓扑结构
解决:回溯历史
核心思想:只记录每个版本核上一个版本不同的地方
举个例子:最大异或和
在这里插入图片描述
解决异或树:用trie,前缀异或
解决R:用可持久化
解决L:对于要走的子树先判断其max_id=L,再进行递归

insert:

  • (前缀下标,处理到第几位,上一个版本,最新版本)
  • 如果上一个版本存在,那么我这现在这个版本的左子树就是上个版本的这个树
  • 把当前这个新版本开辟出来:idx++
  • 继续插入
  • 维护max_id
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
const int N = 600010, M = N * 25;
int n, m;
int s[N];
int tr[M][2], max_id[M];
int root[N], idx;
void insert(int i, int k, int p, int q)
{
	if (k < 0)
	{
		max_id[q] = i;
		return;
	}
	int v = s[i] >> k & 1;
	if (p)tr[q][v ^ 1] = tr[p][v ^ 1];
	tr[q][v] = ++idx;
	insert(i, k - 1, tr[p][v], tr[q][v]);
	max_id[q] = max(max_id[tr[q][0], tr[q][1]], max_id[tr[q][1]]);
}
int query(int root,int c,int l)
{
	int p = root;
	for (int i = 23; i >= 0; i--)
	{
		int v = c >> i & 1;
		if (max_id[tr[p][v ^ 1]] >= l)p = tr[p][v ^ 1];
		else p = tr[p][v];
	}
	return c ^ s[max_id[p]];
	
}

int main()
{
	cin >> n >> m;

	max_id[0] = -1;
	root[0] = ++idx;
	insert(0, 23, 0, root[0]);

	for (int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		s[i] = s[i - 1] ^ x;
		root[i] = ++idx;
		insert(i, 23, root[i - 1], root[i]);
	}

	char op[2];
	int l, r, x;
	while (m--)
	{
		cin >> op;
		if (*op == 'A')
		{
			cin >> x;
			n++;
			s[n] = s[n - 1] ^ x;
			root[n] = ++idx;
			insert(n, 23, root[n - 1], root[n]);

		}
		else
		{
			cin >> l >> r >> x;
			cout << query(root[r - 1], s[n] ^ x, - 1);
		}
	}

}

如果你看到这了
那就祝你平安喜乐,腰缠万贯!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值