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区间 (模拟并查集优化)

复习并查集思想_二叉树最大深度问题_几种做法_并查集/递归/递推/最长路径_多种办法实现

可持久化并查集

并查集模板

CodeForces - 763A(并查集/思维)

POJ2985 并查集+线段树 求第k大的数