3/24 阅读博客三篇+今日《并查集》
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3/24 阅读博客三篇+今日《并查集》相关的知识,希望对你有一定的参考价值。
今日阅读博客:
一道题弄懂递归、深度优先搜索、记忆化搜索、DP动态规划
关于tarjan算法的一些理解(割点割边)
卡特兰数(好像很有用的说)
卡特兰数:C(n)=C(n-1)((4n-2)/(n+1))
C2n (n)=(2n!)/(n!)²
二分图着色技巧:
void color(int u,int pre,int c)
col[u]=c;
for(int i=head[u];i;i=e[i].nxt)
int v=e[i].to;
if(!col[v])
dfs(v,u,3-color); //分成两类
return;
求割点:割点满足条件:
1.改点子树数量>=2
2.对于边(u, v),如果low[v]>=dfn[u],此时u就是割点
void tarjan(int u,int mr)
dfn[u]=low[u]=++tim;
q.push(u);
inq[u]=1;
int rc=0;
for(int i=head[u];i;i=nxt[i])
int v1=v[i];
if(!dfn[v1])
tarjan(v1,mr);
low[u]=min(low[u],low[v1]);
if(low[v1]>=dfn[u]&&u!=mr)
vis[u]=1;
if(u==mr)
rc++;
else if(inq[v1])
low[u]=min(low[u],dfn[v1]);
if(dfn[u]==low[u])
tol++;int tmp;
do
tmp=q.top();
q.pop();
inq[tmp]=0;
while(tmp!=u);
if(u==mr&&rc>=2)
vis[u]=1;
求割边:对于一条边如果它是割边的话,那么low[v] > dfn[u] ,也就是以v为根的子树是封闭的,只要去掉u,v连接的这条边,就会增加联通分量的数量
if(!dfn[v1])
tarjan(v1,mr);
low[u]=min(low[u],low[v1]);
if(low[v1]>dfn[u]&&u!=mr)
vis[u]=1;
else if(inq[v1])
low[u]=min(low[u],dfn[v1]);
求一个数的二进制表示(用补码表示)
void complement(int i,int step)//倒序输出后7位
if (step ==9)return;
f(i >> 1, step + 1);
if (i & 1)cout << "1";
else cout << "0";
int fastpow(int x,int y)
int res=1;
while(y)
if(y&1) res*=x;
x*=x;
y>>=1;
return res;
并查集
1.P1196 [NOI2002] 银河英雄传说
带权并查集,记录集合大小和到头节点的距离。
开两个数组:
dis[i]表示结点i到头节点的距离
sz[i]表示所在集合的大小
#include <bits/stdc++.h>
using namespace std;
const int maxn=6e5+5;
int f[maxn],dis[maxn],sz[maxn],t;
int r_find(int r)
if(f[r]==r)
return f[r];
int k=f[r];
f[r]=r_find(f[r]);
dis[r]+=dis[k]; //当前节点到跟的距离
sz[r]=sz[f[r]]; //更新所在集合大小
return f[r];
int main()
cin>>t;
for(int i=1;i<=30000;i++)
f[i]=i,dis[i]=0,sz[i]=1;
while(t--)
char c;
int i,j,fx,fy;
cin>>c>>i>>j;
fx=r_find(i),fy=r_find(j);
if(c=='M')
f[fx]=fy;
dis[fx]+=sz[fy]; //更新x的根到y的根距离
sz[fx]+=sz[fy]; //更新集合大小
sz[fy]=sz[fx]; //更新
else if(c=='C')
if(fx!=fy)
cout<<"-1"<<endl;continue;
cout<<abs(dis[i]-dis[j])-1<<endl;
return 0;
# P1621 集合
tip:1不是素数
素数的新的求取方式,比较高效
将范围内拥有公因数大于等于因数p的归为一组:
1.求出所有素数,找出大于等于p的进行处理
2.将有大于等于公因数p的在[A,B]内的数归为一类,并查集处理
3.看分出了多少集合
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int prime[maxn],a,b,p,f[maxn],cnt;
bool vis[maxn];
bool check(int x) //结论:大于等于5的素数一定在6的倍数附近
if(x==1||x==4)return 0;
if(x==2||x==3) return 1;
if((x%6!=1)&&(x%6!=5)) return 0;
int k=int(sqrt(x))+1;
for(int i=5;i<=k;i++)
if(x%i==0||x%(i+2)==0) return 0;
return 1;
void init()
for(int i=1;i<=100000;i++)
f[i]=i;
if(check(i)) prime[++cnt]=i;
int r_find(int r)
if(f[r]==r)
return r;
f[r]=r_find(f[r]);
return f[r];
int main()
init(); //筛选素数
/*for(int i=1;i<=cnt;i++)
cout<<prime[i]<<" ";
cout<<endl;*/
scanf("%d%d%d",&a,&b,&p);
for(int i=1;i<=cnt;i++)
if(prime[i]>=p)
int k=ceil(1.0*a/prime[i]);
while(k*prime[i]<=b)
int fx=r_find(prime[i]);
int fy=r_find(prime[i]*k);
f[fy]=fx;
k++;
int ans=0;
for(int i=a;i<=b;i++)
//cout<<r_find(i)<<endl;
if(vis[r_find(i)]==0)
vis[r_find(i)]=1;ans++;
cout<<ans<<endl;
return 0;
3.P1682 过家家
思维+并查集。。。。还好数据量比较小,不然有麻烦了;
1.统计女生的联通块.(每个连通块和男生相连的个数最少的决定能玩几轮)
2.对于每个联通块所连男生数量的统计,需要再开一个数组。
#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e3+5;
struct node
int x,y;
e1[80005],e2[80005];
int n,m,k,ff,f[maxn],out[maxn];
bool mp[maxn][maxn];
int r_find(int r)
if(f[r]==r)
return r;
f[r]=r_find(f[r]);
return f[r];
int main()
scanf("%d%d%d%d",&n,&m,&k,&ff);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d",&e1[i].x,&e1[i].y);
for(int i=1;i<=ff;i++)
scanf("%d%d",&e2[i].x,&e2[i].y); //女生连通块
int fx=r_find(e2[i].x),fy=r_find(e2[i].y);
if(fx!=fy)
f[fx]=fy;
for(int i=1;i<=m;i++)
int v=r_find(f[e1[i].x]);
if(!mp[v][e1[i].y])
out[v]++;
mp[v][e1[i].y]=1;
int mx=inf;
for(int i=1;i<=n;i++)
if(out[i])
mx=min(mx,out[i]);
cout<<min(mx+k,n)<<endl;
return 0;
# P1783 海滩防御
一种利用并查集的新的思维方式
如果一个祖先既和左边界相连也和右边界相连,则说明完全覆盖
#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e6+5;
struct node
int x,y;
double dis;
e[maxn];
int n,m,a[805],b[805],cnt,f[maxn];
double cal(int x1,int y1,int x2,int y2)
return (double)sqrt((x1-x2)*(x1-x2)*1.0+(y1-y2)*(y1-y2)*1.0);
bool cmp(node aa,node bb)
return aa.dis<bb.dis;
int r_find(int r)
if(f[r]==r)
return r;
f[r]=r_find(f[r]);
return f[r];
int main()
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
cin>>a[i]>>b[i],f[i]=i;
for(int i=1;i<m;i++)
for(int j=i+1;j<=m;j++)
e[++cnt].x=i,e[cnt].y=j;
e[cnt].dis=cal(a[i],b[i],a[j],b[j]);
for(int i=1;i<=m;i++)
e[++cnt].x=i,e[cnt].y=0,e[cnt].dis=a[i]*2.0;
e[++cnt].x=i,e[cnt].y=m+1,e[cnt].dis=(n-a[i])*2.0;
f[m+1]=m+1;
sort(e+1,e+cnt+1,cmp);
int ans=0;
for(int i=1;i<=cnt;i++)
int fx=r_find(e[i].x),fy=r_find(e[i].y);
if(fx!=fy)
f[fx]=fy;
if(r_find(0区间 (模拟并查集优化)