有些知识点很零碎,但找起来会很麻烦,就统一放到这里了。
大家如果有些自己比较零碎知识点,也可以发到下面哦,看到了会加进来。
尽量做好分类。
图论
一个边的双连通分量 <=> 任何两个点之间至少存在两个不相交路径
给定一个无向连通图,问最少加几条边,可以将其变为一个边双连通分量
结论:跑一个tarjan缩完点后的那个树中,度数为1的点个数cnt,ans=(cnt+1)/2;
例题195.冗余路径
给定一个有向图,问最少加几条边,可以将其变为一个强连通图
结论;跑一个tarjan缩点后的图中,起点的个数为P,终点的个数为Q,则ans=max(P,Q);
例题 367.学校网络
对一个无向图中的一条边E,如果我们想要得到它的反向边,可以E^1。
如果E=奇数 则反向边=E+1 = E^1
如果E=偶数 则反向边=E-1 = E^1
最大匹配数=点数-最大独立集=点数-最小路径点覆盖
求欧拉回路
同时很重要的单链表删边操作
板子
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10,M = 4e5 + 10;
int n,m,type;
int h[N],e[M],ne[M],idx;
bool used[M];//标记该边是否被用过
int ans[M],cnt;
int din[N],dout[N];//如果是无向边,直接将din,dout相加就是对应的度数
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=h[u])//这里的&i,就相当于h[u]了,如果不想这么写,那么h[u]=ne[i],i=ne[i],也行
{ //因为这一步的优化其实就是,用头结点顶替我们删去的这条边。
if(used[i])//如果这条边用过了,那就顶替掉它;
{
h[u] = ne[i];
continue;
}
used[i]=1;//标记一下被用过了
if(type==1) used[i^1] = 1;
int t;//记录一下走过的边的编号
if(type==1)
{
t = i/2+1;
if(i&1) t = -t;//如果是反向边,需要加负号
}
else t = i+1;
int j = e[i];
h[u]=ne[i];//这里要先删,再进行搜索;
dfs(j);
ans[++cnt] = t;//等搜索结束了,我们再进行赋值
}
}
int main()
{
scanf("%d",&type);
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
if(type==1) add(b,a);
din[b]++,dout[a]++;
}
if(type==1)//矛盾情况,直接输出NO
{
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--) printf("%d ",ans[i]);
puts("");
return 0;
}
位运算
if x=奇数 则x-1=x^1;
if x=偶数 则x+1=x^1
如何计算二进制中中1和0的个数
1的个数:x&(x-1);
0的个数:x|(x-1)
零碎
只交换相邻的元素,使得数组排好序,所需的最少次数是逆序对的数量
对顶堆可以很好的维护中间值。