zoukankan      html  css  js  c++  java
  • 信息传递 题解

    信息传递 题解

    题目给了一堆有向边((i,T_i)),要求最小环

    因为每个点的出度为1,所以每个点只可能在一个环上。那么tarjan就好了

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef unsigned int uint;
    #define rint register int
    #define pb push_back
    //#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2) EOF:*p1++)
    //char buf[1<<21],*p1=buf,*p2=buf;
    inline int rd() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    	return x*f;
    }
    const int N=2e5+10;
    int n,ans=1<<28;
    int st[N],top,timer,dfn[N],low[N],scc[N],C;
    int num_edge,head[N];
    struct edge {
        int to,nxt;
    }e[N];
    void addedge(int from,int to) {
        ++num_edge;
    	e[num_edge].nxt=head[from];
    	e[num_edge].to=to;
    	head[from]=num_edge;
    }
    void tarjan(int u) {
    	low[u]=dfn[u]=++timer,st[++top]=u;
    	for(rint i=head[u];i;i=e[i].nxt) {
    		int v=e[i].to;
    		if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);
    		else if(!scc[v])low[u]=min(low[u],dfn[v]);
    	}
    	if(low[u]==dfn[u]) {
    		scc[u]=++C;int siz=1;
    		while(st[top]!=u)scc[st[top--]]=C,++siz;
    		--top;
    		if(siz>1)ans=min(ans,siz);
    	}
    }
    signed main() {
    	n=rd();
    	for(rint i=1;i<=n;++i)addedge(i,rd());
    	for(rint i=1;i<=n;++i)if(!dfn[i])tarjan(i);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    当然了,用并查集也能做,但是一定要带路径压缩或启发式合并。有些人写法伪了还能AC。具体见https://www.luogu.com.cn/discuss/show/236216

    因为不带路径压缩复杂度错了,所以路径压缩必须带。但是一旦路径压缩就不能通过遍历节点到根的距离来统计答案。那么必须直接把值存下来,在路径压缩前,递归,通过父亲来更新自己的权值(好像叫什么扩展域并查集???)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef unsigned int uint;
    #define rint register int
    #define pb push_back
    //#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2) EOF:*p1++)
    //char buf[1<<21],*p1=buf,*p2=buf;
    inline int rd() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    	return x*f;
    }
    const int N=200010;
    int n,fa[N],dis[N],ans=1<<28;
    int find(int x) {
    	if(x==fa[x])return x;
    	int fx=fa[x];//先存一下,不然没法找
    	fa[x]=find(fa[x]);//先把父亲更新对了
    	dis[x]+=dis[fx];//通过父亲更新自己
    	return fa[x];
    }
    void merge(int x,int y) {
    	int fx=find(x),fy=find(y);
    	if(fx==fy)ans=min(ans,dis[y]+1);
    	else fa[fx]=fy,dis[x]=dis[y]+1;//x接在y下面,同时合并并查集
    }
    signed main() {
    	n=rd();
    	for(rint i=1;i<=n;++i)fa[i]=i;
    	for(rint i=1;i<=n;++i)merge(i,rd());
    	printf("%d
    ",ans);
    	return 0;
    } 
    
    路漫漫其修远兮,吾将上下而求索
  • 相关阅读:
    GridView
    母版页
    Ajax完整结构和删除
    Ajax1
    JQuery动画
    JQuery基础
    LinQ高级查询
    C#简单的学籍管理系统(数据库变更由原来的SQLserver改为SqLite)
    C#两个数只能进行+1,-1,*2操作。求使得两个数相等的最小步骤
    C#求最小公倍数与最大公约数
  • 原文地址:https://www.cnblogs.com/zzctommy/p/13272954.html
Copyright © 2011-2022 走看看