• [2016北京集训测试赛15]statement-[线段树+拆环]


    Description

    Solution

    由于题目要求,将a[i]->b[i](边权为i)后所得的图应该是由森林和环套树组合而成。

    假如是树形结构,所有的t[i]就直接在线段树t[i]点的dfs序(即in[t[i]],out[t[i]]区间)处记录t[i]点的深度。

    这样,针对所有的f[i],在线段树上查找所有包含in[f[i]]点的区间所记录的最大深度d。(这个深度就是在离f[i]最近并且已经验证了是真命题的祖先的深度)

    然后用倍增算出f[i]向上到深度d,所经过的编号最大值c。ans=min(ans,c)。

    原因:ans是指,图中存在a[i]->b[i](1<=i<=ans)时该询问刚好出现矛盾。如果ans-=1,则所有的f[i]都无法到达离它最近的并且已经验证了是真命题的祖先点。

    环套树结构:我们把环拆成树

    其中Ca->Cb的边权还是a->b边的边权。以此类推其他都是这样。(原本环套树上以a,b,c,d,e为根的树还是以a,b,c,d,e为根)如此,假如说t[i]在环上,我们除了在in[t[i]],out[t[i]]区间记录t[i]点的深度,还要在in[C(t[i])],out[C(t[i])]区间记录C(t[i])点的深度。这样,不论是原本以t[i]为根的子树,还是环上的以其他点为根的树的信息都可以更新完毕。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const int N=2e5+10;
    int n,_n,m,Q,a,b;
    int fa1[N<<1],val[N<<1],cir[N];
    int tag[N];//0-not_visit 1-tag the stack 2-out the stack
    int rt[N],cnt;
     
    int y[N<<1],nxt[N<<1],h[N<<1],tot;
    void link(int _x,int _y){y[++tot]=_y;nxt[tot]=h[_x];h[_x]=tot;}
    void build(int u)
    {
        if (!fa1[u]){rt[++cnt]=u;tag[u]=2;return;}
        if (tag[fa1[u]]==1)
        {
            int x=u;
            for(int t=fa1[x];t!=u;t=fa1[t])
            {
                cir[t]=++n;val[n]=val[t];link(n,x);
                fa1[x]=n;x=fa1[x];
            }
            cir[u]=++n;link(n,x);rt[++cnt]=n;
            tag[u]=2;
            return;
        }
        tag[u]=1;
        if (!tag[fa1[u]]) build(fa1[u]);link(fa1[u],u);
        tag[u]=2;
    }
    int dfn,in[N<<1],out[N<<1],dep[N<<1],fa[N<<1][20],mxe[N<<1][20];
    void dfs(int x)
    {
        in[x]=++dfn;mxe[x][0]=val[x];dep[x]=dep[fa[x][0]]+1;
        for (int i=1;i<=19;i++) 
        {
            fa[x][i]=fa[fa[x][i-1]][i-1];
            mxe[x][i]=max(mxe[x][i-1],mxe[fa[x][i-1]][i-1]);
        }
        for (int i=h[x];i;i=nxt[i]) fa[y[i]][0]=x,dfs(y[i]);
        out[x]=dfn;
    }
    int cur[N<<3],mxd[N<<3];
    void modify(int k,int l,int r,int askx,int asky,int d)
    {
        if (askx<=l&&r<=asky){if (Q<cur[k]||mxd[k]<d) cur[k]=Q,mxd[k]=d;return;}
        int mid=(l+r)/2;
        if (askx<=mid) modify(k<<1,l,mid,askx,asky,d);
        if (asky>mid) modify(k<<1|1,mid+1,r,askx,asky,d);
    }
    int query(int k,int l,int r,int x)
    {
        int mid=(l+r)/2,re=cur[k]==Q?mxd[k]:0;
        if (l==r) return re;
        if (x<=mid) re=max(re,query(k<<1,l,mid,x));else re=max(re,query(k<<1|1,mid+1,r,x));
        return re;
    }
    int t,f,ret,ref;
    int main()
    {
        scanf("%d%d",&n,&m);_n=n;
        for (int i=1;i<=m;i++) {scanf("%d%d",&a,&b);fa1[b]=a;val[b]=i;}
        for (int i=1;i<=_n;i++) if (!tag[i]) build(i);
        for (int i=1;i<=cnt;i++) dfs(rt[i]);
        scanf("%d",&Q);
        while (Q--)
        {
            scanf("%d",&ret);
            for (int i=1;i<=ret;i++) 
            {
                scanf("%d",&t),modify(1,1,n,in[t],out[t],dep[t]);
                if (cir[t]) modify(1,1,n,in[cir[t]],out[cir[t]],dep[cir[t]]);
            }
            int ans=m+1,d,c;
            scanf("%d",&ref);
            for (int i=1;i<=ref;i++) 
            {
                scanf("%d",&f);
                d=query(1,1,n,in[f]);c=0;
                if (d==0) continue;else d=dep[f]-d;
                for(int j=0;d;d>>=1,j++) 
                if (d&1) c=max(c,mxe[f][j]),f=fa[f][j];
                ans=min(ans,c);
            }
            if (ans==m+1) printf("OK
    ");else printf("%d
    ",ans);
        }
    }
  • 相关阅读:
    经典网页设计:漂亮的个人作品集网站设计欣赏【中篇】
    引领网页设计潮流的优秀网页作品赏析《第二季》
    Chance – 功能强大的 JavaScript 随机数生成类库
    设计前沿:16款扁平风格 iOS 7 图标设计
    TwentyTwenty – 使用 jQuery 实现图片对比功能
    未来的 Web:九个不可思议的 WebGL 应用试验
    推荐25个帮助你提高技能的 CSS3 实战教程
    经典网页设计:顶尖的个人作品集网站设计欣赏【上篇】
    Smint – 用于单页网站制作的 jQuery 导航菜单插件
    关注经典:CSS Awards 获奖网站作品赏析《第一季》
  • 原文地址:https://www.cnblogs.com/coco-night/p/9715571.html
Copyright © 2020-2023  润新知