• HZOI20190725 B 回家 tarjan


    题目大意:https://www.cnblogs.com/Juve/articles/11226266.html

    题解:

    感觉挺水的,但考场上没打出来

    题目翻译一下就是输出起点到终点必经的点

    其实就是求起点到终点的割点

    这题算法非常多,我介绍我的方法:

    55%:

    可能这个算法是不严谨的,但至少能那55分

    我们先tarjan求点双,然后缩点

    然后我们从1号点所在点双开始dfs,

    维护一个栈,每到一个点双,就把点双中的割点压入栈里,继续搜索

    回溯时在弹栈,当我们搜到n号点所在点双,就停止搜索

    这样栈中剩余的点就是我们要求的点

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    #include<vector>
    #include<stack>
    #define MAXM 4000005
    #define MAXN 2000005
    #define ll long long
    #define re register
    using namespace std;
    inline ll read(){
        re ll x=0;char ch=getchar();
        while(ch<'0'||ch>'9'){ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return x;
    }
    ll t,n,m;
    ll to[MAXM<<2],nxt[MAXM<<2],pre[MAXN],cnt=0;
    inline void add(re ll u,re ll v){
        cnt++,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt;
    }
    ll ver[MAXM<<2],nxtt[MAXM<<2],head[MAXN],sum=0;
    inline void ADD(re ll u,re ll v){
        sum++,ver[sum]=v,nxtt[sum]=head[u],head[u]=sum;
    }
    ll dfn[MAXN],low[MAXN],dfs_order=0,sta[MAXN],top=0,dcc_num=0,belong[MAXN];
    bool is_cut[MAXN];
    vector<ll>dcc[MAXN];
    inline void tarjan(re ll x){
        dfn[x]=low[x]=++dfs_order;
        sta[++top]=x;
        ll num=0;
        for(re ll i=pre[x];i;i=nxt[i]){
            re ll y=to[i];
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
                if(low[y]>=dfn[x]){
                    num++,dcc_num++;
                    if(x!=1||num>1) is_cut[x]=1;
                    re ll z;
                    do{
                        z=sta[top--];
                        dcc[dcc_num].push_back(z);
                    }while(y!=z);
                    dcc[dcc_num].push_back(x);
                }
            }
            else low[x]=min(low[x],dfn[y]);
        }
    }
    stack<ll>a;
    ll tot=0,num=0,new_id[MAXN];
    bool flag=0,ans[MAXN];
    void dfs(ll x,ll fa){
        if(x==belong[n]){
            flag=1;
            return ;
        }
        for(ll i=head[x];i;i=nxtt[i]){
            if(ver[i]==fa) continue;
            ll y=ver[i],N=dcc[y].size();
            for(ll j=0;j<N;j++){
                if(is_cut[dcc[y][j]]){
                    //cout<<dcc[y][j]<<endl;
                    a.push(dcc[y][j]);
                }
            }
            dfs(y,x);
            if(flag) return ;
        }
        //cout<<a.top()<<endl;
        if(!a.empty()) a.pop();
    }
    signed main(){
        t=read();
        while(t--){
            n=read(),m=read();
            for(re ll i=1,u,v;i<=m;i++){
                u=read(),v=read();
                if(u==v) continue;
                add(u,v),add(v,u);
            }
            tarjan(1);
            num=dcc_num;
            for(re ll i=1;i<=n;i++)
                if(is_cut[i]) new_id[i]=++num;
            for(re ll i=1;i<=dcc_num;i++){
                re ll N=dcc[i].size();
                for(re ll j=0;j<N;j++){
                    re ll t=dcc[i][j];
                    if(is_cut[t]){
                        ADD(i,new_id[t]);
                        ADD(new_id[t],i);
                        belong[t]=new_id[t];
                    }else belong[t]=i;
                }
            }
            //for(ll i=1;i<=n;i++)
            //  cout<<i<<' '<<belong[i]<<endl;
            //cout<<"------------------------"<<endl;
            //for(ll i=1;i<=num;i++){
            //  for(ll j=head[i];j;j=nxtt[j]){
            //      cout<<i<<' '<<ver[j]<<endl;
            //  }
            //}
            dfs(belong[1],0);
            while(!a.empty()){
                re ll tp=a.top();
                a.pop();
                if(!ans[tp]){
                    if(tp!=1&&tp!=n){
                        ans[tp]=1;
                        tot++;
                    }
                }
            }
            printf("%lld
    ",tot);
            for(ll i=2;i<n;i++)
                if(ans[i])
                    printf("%lld ",i);
            puts("");
            memset(pre,0,sizeof(pre));
            cnt=dfs_order=tot=dcc_num=sum=num=0;
            memset(dfn,0,sizeof(dfn));
            memset(is_cut,0,sizeof(is_cut));
            memset(dcc,0,sizeof(dcc));
            memset(head,0,sizeof(head));
            memset(ans,0,sizeof(ans));
            flag=0;
        }
        return 0;
    }
    /*
    15 19
    1 2
    1 3
    1 12
    12 13
    13 14
    12 14
    2 4
    3 4
    4 6
    4 5
    6 5
    5 7
    5 8
    7 8
    7 9
    8 9
    9 15
    15 11
    15 10
    
    4 3
    1 2
    2 3
    3 4
     
    5 5
    1 2
    2 3
    3 4
    4 5
    4 1
     
    5 5
    1 2
    2 3
    3 4
    4 5
    5 1
    */
    

    这有什么问题吗?为什么一直55分?有大佬能解释的话可以留言

    另一种算法:

    这个算法一直90分,但找不到问题(是某大佬提出的)

    我们用dijkstra或spfa跑1到n的最短路,顺便记录路径

    那么必经点一定在这条路上

    那么我们就可以求桥,桥两边的点都是要求的点

    100%:

    其实只要在tarjan就割点的板子上加一句话就A了:

    在判割点时,在判断条件后加上dfn[to[i]]<=dfn[n]就可以了

    1 if(low[y]>=dfn[x]&&dfn[y]<=dfn[n]){
    2     num++;
    3     if(x!=1||num>1) is_cut[x]=1,tot++;
    4 }

    想一下tarjan的实现原理,dfn[n]是终点的dfn值

    我们发现如果一个节点y的dfn值大于n,那么n肯定优先于y被搜到,

    而我们是从1开始dfs,所以y肯定不在1到n的必经路上

    那么这道题就非常水了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    #define MAXM 400005
    #define MAXN 200005
    #define ll long long
    #define re register
    using namespace std;
    inline ll read(){
    	re ll x=0;char ch=getchar();
    	while(ch<'0'||ch>'9'){ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    	return x;
    }
    ll t,n,m;
    ll to[MAXM<<1],nxt[MAXM<<1],pre[MAXN],cnt=0;
    inline void add(re ll u,re ll v){
    	cnt++,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt;
    }
    ll dfn[MAXN],low[MAXN],dfs_order=0,tot=0;
    bool is_cut[MAXN];
    inline void tarjan(re ll x){
    	dfn[x]=low[x]=++dfs_order;
    	re ll num=0;
    	for(re ll i=pre[x];i;i=nxt[i]){
    		re ll y=to[i];
    		if(!dfn[y]){
    			tarjan(y);
    			low[x]=min(low[x],low[y]);
    			if(low[y]>=dfn[x]&&dfn[y]<=dfn[n]){
    				num++;
    				if(x!=1||num>1) is_cut[x]=1,tot++;
    			}
    		}
    		else low[x]=min(low[x],dfn[y]);
    	}
    }
    signed main(){
    	t=read();
    	while(t--){
    		n=read(),m=read();
    		for(re ll i=1,u,v;i<=m;i++){
    			u=read(),v=read();
    			if(u==v) continue;
    			add(u,v),add(v,u);
    		}
    		tarjan(1);
    		printf("%lld
    ",tot);
    		for(re ll i=1;i<=n;i++){
    			if(is_cut[i])
    				printf("%lld ",i);
    		}
    		puts("");
    		cnt=dfs_order=tot=0;
    		memset(pre,0,sizeof(pre));
    		memset(dfn,0,sizeof(dfn));
    		memset(is_cut,0,sizeof(is_cut));
    	}
    	return 0;
    }
    

    再来一道loj上类似的题:ZJOI 2004「一本通 3.6 练习 2」嗅探器https://loj.ac/problem/10101

    你会发现这道题非常水,只不过起点终点不是1和n了

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #define MAXM 2005
    #define MAXN 1005
    #define ll long long
    #define re register
    using namespace std;
    ll a, b, n, u, v, ans = 0x7fffffff;
    ll to[MAXM << 1], nxt[MAXM << 1], pre[MAXN], cnt = 0;
    inline void add(re ll u, re ll v) { cnt++, to[cnt] = v, nxt[cnt] = pre[u], pre[u] = cnt; }
    ll dfn[MAXN], low[MAXN], dfs_order = 0, tot = 0;
    bool is_cut[MAXN];
    inline void tarjan(re ll x) {
        dfn[x] = low[x] = ++dfs_order;
        re ll num = 0;
        for (re ll i = pre[x]; i; i = nxt[i]) {
            re ll y = to[i];
            if (!dfn[y]) {
                tarjan(y);
                low[x] = min(low[x], low[y]);
                if (low[y] >= dfn[x] && dfn[y] <= dfn[b]) {
                    num++;
                    if (x != a || num > 1)
                        tot++, ans = min(ans, x);
                }
            } else
                low[x] = min(low[x], dfn[y]);
        }
    }
    signed main() {
        scanf("%lld", &n);
        while (~scanf("%lld%lld", &u, &v)) {
            if (u + v == 0)
                break;
            add(u, v), add(v, u);
        }
        scanf("%lld%lld", &a, &b);
        tarjan(a);
        if (tot)
            printf("%lld
    ", ans);
        else
            puts("No solution");
        return 0;
    }
    
  • 相关阅读:
    notepad++ 在所有行末尾增加符号
    Linux
    [论文阅读] MIR音乐信息检索3
    GitLab CE 常规配置与命令 零点
    Java线程学习之Condition条件
    Java线程学习之ReentrantLock锁
    Java线程学习之读写锁
    PyQt(三) 常见控件二
    PyQt5(四) 线程与绘图处理
    Python 开发规范
  • 原文地址:https://www.cnblogs.com/Juve/p/11246522.html
Copyright © 2020-2023  润新知