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 long
#define fi first
#define se second
#define mp make_pair
#define pii pair<ll,ll>
#define all(x) x.begin(),x.end()
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
#define forn(i,x,g,e) for(int i=g[x];i;i=e[i].next)
using namespace std;//head
const int maxn=1e5+100,maxm=2e5+10;
const ll INF=0x3f3f3f3f,mod=1e9+7;
int casn,n,m,k,num[maxn],root;
class graph{public://@按点@
  struct node{int to,next;}e[maxn];
  int head[maxn],nume,ltop[maxn],fa[maxn],deep[maxn],sz[maxn],son[maxn];
  inline void add(int a,int b){e[++nume]={b,head[a]};head[a]=nume;}
  void init(int n){rep(i,1,n) head[i]=0;nume=0;}
  void dfs1(int now=root,int pre=root,int d=0){
    deep[now]=d,fa[now]=pre,sz[now]=1,son[now]=0;
    forn(i,now,head,e){
      int to=e[i].to;
      if(to!=pre) {
        dfs1(to,now,d+1);
        sz[now]+=sz[to];
        if(sz[to]>sz[son[now]]) son[now]=to;
      }
    }
  }
  void dfs2(int now=root,int pre=root,int sp=root){
    ltop[now]=sp;
      if(son[now])  dfs2(son[now],now,sp);
      forn(i,now,head,e){
        int to=e[i].to;
        if(to!=son[now]&&to!=pre) dfs2(to,now,to);
      }
  }
  void getchain(){dfs1();dfs2();}
  int lca(int x,int y){
    for(;ltop[x]!=ltop[y];deep[ltop[x]]>deep[ltop[y]]?x=fa[ltop[x]]:y=fa[ltop[y]]);
    return deep[x]<deep[y]?x:y;
  }//@基础部分@
  int query(int a,int b){
    int x=lca(a,b);
    int ans=deep[a]+deep[b]-deep[x];
    return ans;
  }
}g;
class domtree{public://@dom为最终的支配树,root为根,cnt为每个点的支配点编号和@
  int dfn[maxn],rev[maxn],anc[maxn];
  int semi[maxn],idom[maxn];
  int fa[maxn],mi[maxn],clo;
  struct node{int to,next;};
  struct graph{
    node e[maxn];int head[maxn],nume;
    void init(int n=maxn-5){nume=0;fill_n(head,n+1,0);}
    void add(int a,int b){e[++nume]={b,head[a]};head[a]=nume;}
  }inv,nxt,dom;
  void  init(int n=maxn-5){
    clo=0;
    rep(i,1,n)fa[i]=mi[i]=semi[i]=i,rev[i]=dfn[i]=anc[i]=idom[i]=0;
    nxt.init(n),inv.init(n),dom.init(n);
  }
  void add(int a,int b){inv.add(b,a),nxt.add(a,b);}
  int find(int now){
    if(fa[now]==now) return now;
    int fx=fa[now],y=find(fa[now]);
    if(dfn[semi[mi[fx]]]<dfn[semi[mi[now]]])
      mi[now]=mi[fx];
    return fa[now]=y;
  }
  void tdfs(int now){
    dfn[now]=++clo;rev[clo]=now;
    forn(i,now,nxt.head,nxt.e)if(!dfn[nxt.e[i].to])
      anc[nxt.e[i].to]=now,tdfs(nxt.e[i].to);
  }
  void maketree(int root,int n=maxn-5){
    tdfs(root);
    per(i,2,n){
      int now=rev[i],tmp=n;
      forn(i,now,inv.head,inv.e){
        int to=inv.e[i].to;if(!dfn[to]) continue;
        if(dfn[to]<dfn[now]) tmp=min(tmp,dfn[to]);
        else find(to),tmp=min(tmp,dfn[semi[mi[to]]]);
      }
      semi[now]=rev[tmp];fa[now]=anc[now];
      dom.add(semi[now],now);
      now=rev[i-1];
      forn(i,now,dom.head,dom.e){
        int to=dom.e[i].to;find(to);
        if(semi[mi[to]]==now) idom[to]=now;
        else idom[to]=mi[to];
      }
    }
    rep(i,2,n){
      int to=rev[i];
      if(idom[to]!=semi[to]) idom[to]=idom[idom[to]];
    }
    dom.init(n);
    rep(i,1,n) if(i!=root)g.add(idom[i],i);
  }
}tree;
int cntin[maxn];
int main() {IO;
  cin>>casn;
  while(casn--){
    cin>>n>>m;
    root=n+1;
    tree.init(root);
    g.init(root);
    rep(i,1,n) cntin[i]=0;
    rep(i,1,m){
      int a,b;cin>>a>>b;
      tree.add(b,a);
      cntin[a]++;
    }
    rep(i,1,n)if(!cntin[i])tree.add(root,i);
    tree.maketree(root,root);
    g.getchain();
    cin>>m;
    while(m--){
      int a,b;cin>>a>>b;
      cout<<g.query(a,b)<<endl;
    }
  }
}

所以用针对DAG的拓扑排序+倍增做法,就能1000ms以下ac了(一个题套了两个板子..)

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define fi first
#define endl ‘\n‘
#define se second
#define mp make_pair
#define pii pair<ll,ll>
#define all(x) x.begin(),x.end()
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
#define forn(ii,x) for(int ii=head[x];ii;ii=e[ii].next)
#pragma GCC optimize("Ofast")
#define forn(i,x,g,e) for(int i=g[x];i;i=e[i].next)
const int maxn=3e5+9,maxm=1e9+10,maxp=20;
using namespace std;
int casn,n,m,k,root;
int cntin[maxn];
struct node{int to,next;};
class graph{public:
  node e[maxn];int head[maxn],nume;
  void init(int n=maxn-5){nume=0;fill_n(head,n+1,0);}
  void add(int a,int b){e[++nume]={b,head[a]};head[a]=nume;}
}inv,nxt,dom;
class domtree{public://@DAG版本,dom依旧是最终树@
  int deep[maxn],anc[maxn][maxp],que[maxn];
  vector<int>edge;
  void init(int n=maxn-5){
    inv.init(n),nxt.init(n),dom.init(n);
    edge.clear();
  }
  void bit(int &x,int h){
    for(int i=0;h>0;++i){
      if(h&1) x=anc[x][i];
      h>>=1;
    }
  }
  int lca(int a,int b){
    if(deep[a]<deep[b]) swap(a,b);
    bit(a,deep[a]-deep[b]);
    if(a==b) return a;
    per(i,0,maxp-1) if(anc[a][i]!=anc[b][i])
      a=anc[a][i],b=anc[b][i];
    return anc[a][0];
  }
  void tpsort(int n){
    int tp=0,ed=0;
    rep(i,1,n) {
      if(!cntin[i]) {
        que[ed++]=i;
        inv.add(0,i);
        nxt.add(i,0);
        edge.push_back(i);
      }
    }
    while(ed!=tp){
      int now=que[tp++];
      forn(i,now,inv.head,inv.e){
        int to=inv.e[i].to;
        cntin[to]--;
        if(!cntin[to]) que[ed++]=to,edge.push_back(to);
      }
    }
  }
  void maketree(){
    for(auto i:edge){
      int fa=-1;
      forn(j,i,nxt.head,nxt.e){
        int to=nxt.e[j].to;
        if(fa==-1) fa=to;
        else fa=lca(fa,to);
      }fa=fa==-1?0:fa;
      deep[i]=deep[fa]+1,anc[i][0]=fa;
      rep(j,1,maxp-1) anc[i][j]=anc[anc[i][j-1]][j-1];
      dom.add(fa,i);
    }
  }
  int query(int a,int b){return deep[a]+deep[b]-deep[lca(a,b)];}
}tree;

int main() {IO;
  cin>>casn;
  while(casn--){
    cin>>n>>m;
    tree.init(n+1);
    while(m--){
      int a,b;cin>>a>>b;
      nxt.add(a,b);
      inv.add(b,a);
      cntin[a]++;
    }
    tree.tpsort(n);
    tree.maketree();
    cin>>m;
    while(m--){
      int a,b;cin>>a>>b;
      cout<<tree.query(a,b)<<endl;
    }
  }
}

原文地址:https://www.cnblogs.com/nervendnig/p/11282146.html

时间: 2024-08-29 13:52:39

hdu 6604 DAG上的支配树(灭绝树)的相关文章

灭绝树(支配树)

DAG建立支配树 原图T的基础上建立TR 将T进行拓扑排序(若T初始有多个入度为0的结点可以先用一个虚拟根将他们链接起来) 顺序扫描拓扑排序,假设当前点x,在TR中,x的祖先的支配点已经建立好,所以找到x的所有直接祖先的lca就是x的支配点f.在图D中将f->x连边 有向图支配树建立步骤 原图E的基础上建立反图G dfs一次得到dfs序,建立dfs树T 倒着扫描dfs序上的点x,借助反图和并查集找出semi[x].将T中连一条semi[x]-> x的边 此时T为DAG DAG的基础上进行\(O

hdu 3974 线段树 将树弄到区间上

Assign the task Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1647    Accepted Submission(s): 753 Problem Description There is a company that has N employees(numbered from 1 to N),every emplo

bzoj 2815 灭绝树

对于一个食物网(一个DAG),一个物种死亡后,某些物种就必然死亡,求出必然死亡的是那些物种. 灭绝树的另一种含义是:“灭绝树跟节点到节点u的路径上的节点由那些原图中从根节点到节点u的所有路径中都经过了的点“. 1 /************************************************************** 2 Problem: 2815 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:424 ms

HDU 4718 The LCIS on the Tree(树链剖分)

Problem Description For a sequence S1, S2, ... , SN, and a pair of integers (i, j), if 1 <= i <= j <= N and Si < Si+1 < Si+2 < ... < Sj-1 < Sj , then the sequence Si, Si+1, ... , Sj is a CIS(Continuous Increasing Subsequence). The

hdu 5172 GTY&#39;s gay friends(线段树最值)

题意: GTY有n个朋友,站成一排,每个人有一个特征值ai. 有m个询问.每次询问给两个数L,R.问你[L,R](即aL...aR)是否是1..(R-L+1)的一个全排列. 是输出YES,否则输出NO 思路: 先判断是否segma(a[L,R])是否等于(R-L)*(R-L+1)/2. 记录每一个ai上一次的位置pre[i]. 这样只要判断a[L]...a[R]中的每一个pre[i]是否都小于L即可.(这个方法太妙) 线段树区间求最大值. *用map效率明显下降. 代码: int const N

HDU 4942 Game on S♂play(线段树、模拟、扩栈)

比赛的时候想到这题的大概做法,但由于卡别的水题...就赛后做了... 题意:给一个二叉树,每个结点有一个w[i],有3种操作,0 x表示左旋x,1 x表示右旋x,3 x表示询问x结点的价值,其中,价值为x子树结点的累加价值的累乘,其中,结点的累加价值为结点子树的Σw[i].即询问是,∏Σw. 好像题意被我说得好渣好乱....还是忽略上面2行吧... 首先,左旋右旋不影响中序遍历的index,即,可以把题目那2个图进行中序遍历,结果都是αXβYγ.由此,可以建立线段树. 而左旋右旋的过程模拟即可,

HDU 4417 Super Mario ( 超级马里奥 + 主席树 + 线段树/树状数组离线处理 + 划分树 )

HDU 4417 - Super Mario ( 主席树 + 线段树/树状数组离线处理 + 划分树 ) 这道题有很多种做法,我先学习的是主席树.后面陆续补上线段树离线和划分树 题目大意就是给定一个区间给定一个数列,每次要求你查询区间[L,R]内不超过K的数的数量 主席树做法: 最基本的是静态第k大,这里是求静态的 <= K,差不多,在Query操作里面需要修改修改 先建立size棵主席树,然后询问的时候统计的是 第R棵主席树中[1,K]的数量 - 第L-1棵主席树中[1,K]的数量 注意这里下标

HDU 4902 Nice boat 多校4 线段树

给定n个数 第一个操作和普通,区间覆盖性的,把l-r区间的所有值改成固定的val 第二个操作是重点,输入l r x 把l-r区间的所有大于x的数,变成gcd(a[i],x) a[i]即指满足条件的序列上的数值 最后才输出所有值 当时苦思这个地方如何优化,想着不可能单点去更新吧,但是区间gcd,不能保存下来,一来他是要>x才有效,本来聪哥想了一种先把各种x gcd一遍,最后在push下去,发现不行,就是因为他对>x才有效,你在区间就直接gcd了,那不是把不该gcd的也给搞了 还想过说先存起来所有

hdu 1556:Color the ball(第二类树状数组 —— 区间更新,点求和)

Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8984    Accepted Submission(s): 4594 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球