一些零碎知识点(持续更新)
有些知识点很零碎,但找起来会很麻烦,就统一放到这里了。
大家如果有些自己比较零碎知识点,也可以发到下面哦,看到了会加进来。
尽量做好分类。
图论
一堆(大于等于2)拓扑图边变为,强连通图,只需加max(起点个数,终点个数)条边
一个边的双连通分量 <=> 任何两个点之间至少存在两个不相交路径
给定一个无向连通图,问最少加几条边,可以将其变为一个边双连通分量
结论:跑一个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)
DP
用最少的下降子序列把整个序列全部覆盖的个数=最长上升子序列的元素个数
搜索
A*经验积累
IDA*经验积累
当需要搜索的终点是一个固定的结构的话,我们可以检查还有多少并未到应去的位置的数量,以此来估值,一定比实际要动的还要小。
例如,八数码和骑士精神
数学
求前n个数的约数和
时间复杂度\(O(nlogn)\)
for(int i=1;i<=n;i++)
for(int j=2;j<=n/i;j++)//i是进行拓展的数,j是拓展倍数,j不能从1开始,因为i加的数不包括本身。
sum[i*j]+=i;
零碎
只交换相邻的元素,使得数组排好序,所需的最少次数是逆序对的数量
去重边时,可以用hash表,unordered_set,其中存的是hash值,
例如,a->b,则可以记录为hash=a*100000+b,代表两点之间已有一条边。
对顶堆可以很好的维护中间值。
解决环形问题时,可以将区间向后复制一次,接着对整个区间进行操作,就能起到环形的效果

浙公网安备 33010602011771号