• CF757F Team Rocket Rises Again——最短路+支配树


    CF757F Team Rocket Rises Again


    全体起立,全体起立,这是我A的第一道黑题(虽然是CF的);

    来一波番茄攻击;

     

    不扯淡了,这道题也是学习支配树(之前)应该做的题;

    灾难不同的是,那个是直接给你有向图,这里给的是无向图;

    我们要求的是删除一个点会造成多少点的最短路发生变化,那么我们可以根据最短路再建一个有向图,这样就和灾难一样了;

    很不幸,我建了四个图;

    因为一开始写挂了所以图的编号是乱的;(这并不影响我AC)
    add是无向图,add2是有向图,add4是反图,add3是建出来的支配树;

    首先我们为什么要建反图:

    {

    一个点是否对其他的点有价值,就是看是否他在最短路径上;

    我们根据有向图算出了拓扑序,拓扑序靠前的就是靠近起始点的;

    我们需要建支配树,那么当前点要当谁的儿子?

    当然是所有连向他的点的LCA,因为所有连向他的点删掉后(点数大于等于2)还可以在其他的点上走;

    我们当初只建了一个有向图连向当前点,我们还要回去找LCA,所以要建一个反图;
    }

    然后子树大小即为答案;(取最大值)

    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn=3e5+10;
    int n,m,s;
    int pre[maxn*2],last[maxn],other[maxn*2],l;
    ll len[maxn*2];
    int pre2[maxn*2],last2[maxn],other2[maxn*2],l2;
    int pre3[maxn*2],last3[maxn],other3[maxn*2],l3;
    int pre4[maxn*2],last4[maxn],other4[maxn*2],l4;
    void add(int x,int y,ll z)
    {
        l++;
        pre[l]=last[x];
        last[x]=l;
        other[l]=y;
        len[l]=z;
    }
    void add2(int x,int y)
    {
        l2++;
        pre2[l2]=last2[x];
        last2[x]=l2;
        other2[l2]=y;
        //len[l]=z;
    }
    
    void add3(int x,int y)
    {
        l3++;
        pre3[l3]=last3[x];
        last3[x]=l3;
        other3[l3]=y;
        //len[l]=z;
    }
    void add4(int x,int y)
    {
        l4++;
        pre4[l4]=last4[x];
        last4[x]=l4;
        other4[l4]=y;
    }
    priority_queue<pair<ll,int> > qq;
    bool vis[maxn];
    ll dis[maxn];
    void dijkstra(int x)
    {
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        dis[x]=0;
        qq.push(make_pair(0,x));
        while(!qq.empty())
        {
            int u=qq.top().second;
            qq.pop();
            if(vis[u]) continue;
            vis[u]=1;
            for(int p=last[u];p;p=pre[p])
            {
                int v=other[p];
                if(dis[v]>dis[u]+len[p])
                {
                    dis[v]=dis[u]+len[p];
                    qq.push(make_pair(-dis[v],v));
                }
            }
        }
    }
    queue<int> q;
    int topo[maxn],sum,in_eage[maxn];
    void toposort()//此时用有向图找出拓扑序 
    {
        for(int i=1;i<=n;i++)
        {
            if(in_eage[i]==0&&dis[i]!=dis[0]) q.push(i);//此时要抛弃不在有向图上的点 
        }
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            topo[++sum]=x;
            for(int p=last2[x];p;p=pre2[p])
            {
                int v=other2[p];
                in_eage[v]--;
                if(in_eage[v]==0)
                {
                    q.push(v);
                }
            }
        }
    }
    int father[maxn],f[maxn][200];
    int dep[maxn];
    
    void rmq(int x)
    {
        f[x][0]=father[x];
        for(int i=1;i<=17;i++)
        {
            f[x][i]=f[f[x][i-1]][i-1];
        }
    }
    
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y]) swap(x,y);
        for(int j=0;j<=17;j++)
        {
            if((dep[x]-dep[y])&(1<<j)) x=f[x][j];
        }
        if(x==y) return x;
        for(int j=17;j>=0;j--)
        {
            if(f[x][j]!=f[y][j])
            {
                x=f[x][j];
                y=f[y][j];
            }
        }
        return father[x];
    }
    
    void build()//重新建树的时候用反图 
    {
        father[n+1]=n+1;//建立虚拟节点 
        f[n+1][0]=n+1;
        dep[n+1]=1;
        for(int i=1;i<=n;i++)//拓扑序从小到大开始遍历,根据反图,当前建的树的子树大小即为支配数 
        {
            if(!topo[i]) break;//并不是所有点都在有向图上 ,但是图上的点拓扑序是连续的 
            int x=topo[i];
            if(!last4[x])//将没有出边的点连上虚拟节点 
            {
                dep[x]=2;
                father[x]=n+1;
                f[x][0]=n+1;
                add3(n+1,x);//3为生成的支配树 
                continue;
            }
            
            int lca=other4[last4[x]];
            for(int p=last4[x];p;p=pre4[p])
            {
                int v=other4[p];
                lca=LCA(lca,v);
            }
            father[x]=lca;
            dep[x]=dep[lca]+1;
            add3(lca,x);
            rmq(x);
        }
    }
    
    int siz[maxn];
    int ans;
    void dfs(int x)
    {
        siz[x]=1;
        for(int p=last3[x];p;p=pre3[p])
        {
            int v=other3[p];
            dfs(v);
            siz[x]+=siz[v];
        }
        if(x!=n+1&&x!=s) ans=max(ans,siz[x]);
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&s);
        for(int i=1;i<=m;i++)
        {
            int x,y;ll z;
            scanf("%d%d%lld",&x,&y,&z);
            add(x,y,z);
            add(y,x,z);//建无向图 
        }
        dijkstra(s);//求最短路 
        for(int i=1;i<=n;i++)
        {
            for(int p=last[i];p;p=pre[p])
            {
                int v=other[p];
                if(dis[v]==dis[i]+len[p])
                {
                    add2(i,v);//根据最短路建立有向图 
                    add4(v,i); //反图 
                    in_eage[v]++;//rudu 
                }
            }
        }
        toposort();
        build();
        dfs(n+1);
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    单元测试,集成测试与系统测试
    关于 单窗口服务模型模拟 进行的小测试
    软件测试新随笔
    白盒测试
    黑盒测试小实验
    JUnit框架初次体验
    等价类划分进阶篇
    等价类划分
    因果图法测试小例
    android中将EditText改成不可编辑的状态
  • 原文地址:https://www.cnblogs.com/WHFF521/p/11606423.html
Copyright © 2020-2023  润新知