• hdu 4297 One and One Story 夜


    http://acm.hdu.edu.cn/showproblem.php?pid=4297

    此题目中的图是一个特殊的森林  特殊在于它的树都有一个环 这个环内点包括树根

    基本思路:

    先用并查集 对图进行处理

    1  建立完整森林

    2  构成环的边不加入森林

    3  记录每个环上有多少点  每个环上点属于第几个环

    然后处理 couples

    不在一个树上的不可达

    同一个 room 内 特别处理

    将 couples 储存

    然后LCA  这里LCA 与原始LCA 有不同

    如果最近公共祖先不在环上 直接求出 如果在环上 求其分别是环上哪一个点过来的

    最后就好处理了

    对不同方案进行选优

    代码及其注释:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <vector>
    #include <algorithm>
    
    #define LL long long
    //外挂开栈
    #pragma comment(linker, "/STACK:1024000000,1024000000")
    using namespace std;
    
    const int N=500005;
    vector<int>qtnum[N];//表示有此点 的couple 是第几个couple
    vector<int>treehead;//每个数的树根
    vector<int>cirnodenum;//一个树对应一个环 这个环的点个数
    struct node1{
        int l,r;
        int lf,rf;
    }qt[N];//couples l,r 如果公共最近父节点不在环上 lf,rf则相等 
          //否则对应表示从环上哪个点过来
    int head[N];
    struct node
    {
        int j,next;
    }side[N];//邻接表存森林
    int I;
    int f[N];//并查集使用
    int pre[N];//每个点对应的前驱结点
    int cir[N],T;//每个点对应第几个环 不在环上为 -1 T用来环计数
    int dist[N];//每个点到树根的距离
    int Findf(int x)
    {
        if(x!=f[x])
        f[x]=Findf(f[x]);
        return f[x];
    }
    void Build(int i,int j)
    {
        side[I].j=j;
        side[I].next=head[i];
        head[i]=I++;
    }
    void Findcir(int boot,int k)//求环内点个数 及环上点标记在第几个环上
    {
        int sum=0;
        while(k!=boot){
            ++sum;
            cir[k]=T;
            k=pre[k];
        }
        ++sum;
        cirnodenum.push_back(sum);
        cir[k]=T++;
    }
    void Searchqt(int l,int k)//找 couples 的lf 和 rf
    {
        for(unsigned int i=0;i<qtnum[l].size();++i)
        {
            int j=qtnum[l][i];
            if(l==qt[j].l)
            {
                if(f[qt[j].r]!=-1)
                {
                    int ftemp=Findf(qt[j].r);
                    if(cir[ftemp]!=-1)qt[j].lf=k;
                    else qt[j].lf=ftemp;
                    qt[j].rf=ftemp;
                }
            }else
            {
                if(f[qt[j].l]!=-1)
                {
                    int ftemp=Findf(qt[j].l);
                    if(cir[ftemp]!=-1)qt[j].rf=k;
                    else qt[j].rf=ftemp;
                    qt[j].lf=ftemp;
                }
            }
        }
    }
    void dfs(int x,int pre,int k,int d)//LCA
    {
        dist[x]=d;
        if(cir[x]!=-1)//从环上哪个点上过来 如果此点在环上 更新
        k=x;
        Searchqt(x,k);
        f[x]=x;
        for(int t=head[x];t!=-1;t=side[t].next)
        {
            int l=side[t].j;
            dfs(l,x,k,d+1);
        }
        if(cir[x]==-1)//环上的点不在想前指向
        f[x]=pre;
    }
    void Lca()
    {
        memset(f,-1,sizeof(f));
        for(unsigned int i=0;i<treehead.size();++i)
        dfs(treehead[i],treehead[i],-1,0);
    }
    int main()
    {
        //freopen("data.txt","r",stdin);
        int n,K;
        while(scanf("%d %d",&n,&K)!=EOF)
        {
            memset(head,-1,sizeof(head));
            memset(cir,-1,sizeof(cir));
            I=0;T=0;
            for(int i=1;i<=n;++i)
            {f[i]=i;qtnum[i].clear();}
            treehead.clear();cirnodenum.clear();
            for(int i=1;i<=n;++i)
            {
                scanf("%d",&pre[i]);
                if(Findf(i)!=Findf(pre[i]))//是否有环
                {
                    f[Findf(i)]=Findf(pre[i]);
                    Build(pre[i],i);
                }
                else
                {
                    Findcir(i,pre[i]);//有环的话 就记录相关信息
                    treehead.push_back(i);
                }
            }
            for(int i=0;i<K;++i)//对couples 进行取舍 处理记录
            {
                scanf("%d %d",&qt[i].l,&qt[i].r);
                if(Findf(qt[i].l)!=Findf(qt[i].r))
                {qt[i].lf=qt[i].rf=-1;continue;}
                if(qt[i].l==qt[i].r)
                {qt[i].lf=qt[i].rf=0;continue;}
                if(cir[qt[i].l]!=-1&&cir[qt[i].r]!=-1)
                {qt[i].lf=qt[i].l;qt[i].rf=qt[i].r;continue;}
                qtnum[qt[i].l].push_back(i);
                qtnum[qt[i].r].push_back(i);
            }
            Lca();
            int ansa,ansb,A,B,atemp,btemp;
            int maxa,maxb,mina,minb;
            for(int i=0;i<K;++i)//找各种方案最优
            {
                if(qt[i].lf<=0)
                {printf("%d %d\n",qt[i].lf,qt[i].rf);continue;}
                A=dist[qt[i].l]-dist[qt[i].lf];
                B=dist[qt[i].r]-dist[qt[i].rf];
                if(qt[i].lf==qt[i].rf)
                {printf("%d %d\n",A,B);continue;}
                atemp=dist[qt[i].lf]-dist[qt[i].rf];
                if(atemp<0)atemp+=cirnodenum[cir[qt[i].lf]];
                btemp=dist[qt[i].rf]-dist[qt[i].lf];
                if(btemp<0)btemp+=cirnodenum[cir[qt[i].rf]];
                maxa=max(A+atemp,B);
                maxb=max(A,B+btemp);
                mina=min(A+atemp,B);
                minb=min(A,B+btemp);
                if(maxa<maxb||(maxa==maxb&&(mina<minb||(mina==minb&&A+atemp>=B))))
                {ansa=A+atemp;ansb=B;}
                else
                {ansa=A;ansb=B+btemp;}
                printf("%d %d\n",ansa,ansb);
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    racle SQL性能优化
    Oracle 删除重复数据只留一条
    oracle存储过程常用技巧
    详解:数据库名、实例名、ORACLE_SID、数据库域名、全局数据库名、服务名及手工脚本创建oracle数据库
    用友ERP-U8最新破解(再次更新版本,附安装过程中的解决办法)
    轻松三步教你配置Oracle—windows环境
    非常好的Oracle教程【转】
    Oracle新表使用序列(sequence)作为插入值,初始值不是第一个,oraclesequence
    大数据学习资源汇总
    Index
  • 原文地址:https://www.cnblogs.com/liulangye/p/2694305.html
Copyright © 2020-2023  润新知