HDU.4694.Important Sisters(支配树)

HDU

\(Description\)

给定一张简单有向图,起点为\(n\)。对每个点求其支配点的编号和。
\(n\leq 50000\)。

\(Solution\)

支配树。

还是有点小懵逼。
不管了,说不定会讲,反正以后再说。

https://blog.csdn.net/litble/article/details/83019578
有图的:https://blog.csdn.net/VioletSu/article/details/81041954
有题的:https://blog.csdn.net/L_0_Forever_LF/article/details/79386508
有怎么卡纯路径压缩并查集的:https://www.cnblogs.com/meowww/archive/2017/02/27/6475952.html

记几个名词:
\(Lengauer\ Tarjan\)算法。
半支配点(\(semi-dominator\)),记作\(semi(x)\)。
最近支配点(\(immediate\ dominator\)),记作\(idom(x)\)。



想不到我竟然也有把if(x==y)写成if(x=y)而且还调半天的时候...

//1107MS    8292K
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=50005,M=1e5+5;

int Index,dfn[N],ref[N],F[N],fa[N],mn[N],semi[N],idom[N],Ans[N];
struct Graph
{
    int Enum,H[N],nxt[M],to[M];
    inline void Clear(int n)
    {
        Enum=0, memset(H,0,n+1<<2);
    }
    inline void AE(int u,int v)
    {
        to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    }
}G,RG,SG,T;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
void DFS0(int x)
{
    ref[dfn[x]=++Index]=x;
    for(int i=G.H[x],v; i; i=G.nxt[i])
        if(!dfn[v=G.to[i]]) fa[v]=x, DFS0(v);
}
int Find(int x)
{
    if(x==F[x]) return x;
    int tmp=F[x];
    F[x]=Find(F[x]);
    if(dfn[semi[mn[tmp]]]<dfn[semi[mn[x]]]) mn[x]=mn[tmp];
    return F[x];
}
void DFS(int x,int s)
{
    Ans[x]=s+=x;
    for(int i=T.H[x]; i; i=T.nxt[i]) DFS(T.to[i],s);
}
void Solve(int n)
{
    for(int k=n; k>1; --k)
    {
        int x=ref[k],t=n;//求半支配点
        for(int i=RG.H[x],v; i; i=RG.nxt[i])
            if(dfn[v=RG.to[i]])
                if(dfn[v]<dfn[x]) t=std::min(t,dfn[v]);
                else Find(v), t=std::min(t,dfn[semi[mn[v]]]);
        F[x]=fa[x], SG.AE(semi[x]=ref[t],x);

        x=ref[k-1];//从半支配点到支配点
        for(int i=SG.H[x],v; i; i=SG.nxt[i])
        {
            Find(v=SG.to[i]);
            if(semi[v]==semi[mn[v]]) idom[v]=semi[v];
            else idom[v]=mn[v];//idom[mn[v]]此时可能并未找到
        }
    }
    for(int k=2,x; k<=n; ++k)
    {
        x=ref[k];
        if(idom[x]!=semi[x]) idom[x]=idom[idom[x]];
        T.AE(idom[x],x);
    }
    DFS(n,0);
    for(int i=1; i<n; printf("%d ",Ans[i++]));
    printf("%d\n",Ans[n]), memset(Ans,0,n+1<<2);
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1,u,v; i<=m; ++i) u=read(),v=read(),G.AE(u,v),RG.AE(v,u);
        for(int i=1; i<=n; ++i) F[i]=semi[i]=mn[i]=i;
        Index=0, DFS0(n), Solve(n);
        G.Clear(n), RG.Clear(n), SG.Clear(n), T.Clear(n);
        memset(dfn,0,n+1<<2), memset(idom,0,n+1<<2), memset(ref,0,n+1<<2);//, memset(fa,0,n+1<<2);//不都清空会RE啊==
    }
    return 0;
}

原文地址:https://www.cnblogs.com/SovietPower/p/10123414.html

时间: 2024-11-13 07:56:44

HDU.4694.Important Sisters(支配树)的相关文章

hdu 6604 DAG上的支配树(灭绝树)

http://acm.hdu.edu.cn/showproblem.php?pid=6604 很裸的支配树/灭绝树题 一般图的tarjan算法的话,先建立,反向图,然后建立一个超级源点,然后连到几个起点,跑支配树就行 可惜太慢...过不去 #pragma GCC optimize("Ofast") #include<bits/stdc++.h> #define endl '\n' #define ll long long #define ull unsigned long

hdu 4964 支配树

支配树裸题:求支配集 不太会写带权并查集,调了好久 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 #define maxn 100020 8 #define inf 0x3f3f3f3f 9 10 typedef long long ll; 1

hdu 2795 Billboard(线段树)

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10890    Accepted Submission(s): 4827 Problem Description At the entrance to the university, there is a huge rectangular billboard of

HDU 4902 Nice boat(线段树)

HDU Nice boat 题目链接 题意:给定一个序列,两种操作,把一段变成x,把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 思路:线段树,每个结点多一个cover表示该位置以下区间是否数字全相同,然后每次延迟操作,最后输出的时候单点查询即可 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1

HDU 3966 Aragorn&#39;s Story (树链点权剖分,成段修改单点查询)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966 树链剖分的模版,成段更新单点查询.熟悉线段树的成段更新的话就小case啦. 1 //树链剖分 边权修改 单点查询 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #include <cstdio> 6 using namespace std; 7 const int M

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

Hdu 3699 Aragorn&#39;s Story (树链剖分)

题目大意: 对一颗树上进行路径加减,然后询问单点的值. 思路分析: 简单的树链剖分模板题. #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #pragma comment(linker,"/STACk:10240000,10240000") #define maxn 50005 #define lson num<<1,s

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

hdu 3015 Disharmony Trees (离散化+树状数组)

Disharmony Trees Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 663    Accepted Submission(s): 307 Problem Description One day Sophia finds a very big square. There are n trees in the square. T