【JZOJ5705】【GDSOI2018 day3】谁是冠军(champion)(主席树+tarjan)

Problem

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Hint

这里写图片描述

Solution

60points:暴力 or tarjan

  首先,考虑转换模型。对于两个人x,y,如果x能胜y,则从x向y连一条边;反之则反。那么原题就转化为了给出一个竞赛图,求能够遍历到所有点的点。
  可以枚举每个点x,从x开始遍历,判断能否可以遍历到所有点。这样暴力的复杂度为 O ( n 2 )
  当然,我们也可以tarjan一下,然后取出拓扑序最前的分量。注意,这只会取出一个分量。因为根据竞赛图的性质,缩完了点之后,任意两个分量间依然会有至少一条边。这样做的复杂度为 O ( n + n 2 )
  时间复杂度: O ( n 2 )

100points:主席树优化连边

  上述做法的瓶颈在于连边。因为我们始终要连 O ( n 2 ) 条边,所以时间复杂度始终不能少于边数。
  那么其实我们可以用主席树优化连边。
  不要看到“主席树”这三个字就怕了,因为本题不须打树套树,所以应该也不算主席树;况且打起来也非常简单。不过如果你一点都不会还是戳一戳这里吧。
  首先,由于是两项指标都大于就可以造成连边,所以原题可以转化为a,b的贡献,b,c的贡献以及c,a的贡献。
  我们先考虑a,b两项指标造成的连边。
  首先,按a将所有人从小到大排序。然后顺序扫一遍。每扫到一个人i,我们就从点i连向权值线段树中[1,bi-1]这个区间内所有的完整区间(可以分成 l o g 2 n 个完整区间)连边(线段树中每一个点代表一个区间);然后从所有包含bi的区间向i连边(至多有 l o g 2 n 个区间包含bi)。
  这样连边会有问题。比如说有x,y,z三个人,x的指标为{2,3},y的指标为{1,1},z的指标为{3,2}。从小到大排序后,顺序为y,x,z。先将y插入by也即1这个位置;查询x;此时,我们又要将z插入bz也即2这个位置。但如果我们插在这个位置,那么我们又从x连向了[1,3-1]也即[1,2]这个区间,然后[1,2]这个区间又包含bz,所以[1,2]会连向z,于是我们可以从x走到z;但x事实上不能胜z。
  所以说,我们要采用主席树的思想,也就是每次插点的时候,我们在线段树上经过的那条链都要新开点。
  然后再说一说接下来的。如果你和一开始的我一样,傻逼逼地tarjan缩点、拓扑排序,那样不仅会MLE、TLE,还会打得奇长无比。
  那么其实我们可以不必缩点、拓扑排序。我们在tarjan的时候,每当我们找到一个分量,我们就把答案更新为此分量中的、表示人的点。
  因为如果分量c可以到达所有点,那么当我们遍历到c的时候,我们就搜完了所有的点,然后递归回溯回来的时候,c是最后一层递归,我们就会把答案更新为c;而如果c不能,那么我们暂时把答案赋为c,但是最后还是会被更新为正确答案。
  时间复杂度: O ( n l o g 2 n )

Code

  槽点:JZOJ上,n log n≈1660964个点的递归会爆栈,害得我又要手打人工栈。。。o(╥﹏╥)o

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>
using namespace std;
#define A t[v].l
#define B t[v].r
#define fo(i,a,b) for(i=a;i<=b;i++)
const int N=1e5+1,D=6e6,M=D<<1;
int i,j,n;
struct node
{
    int i,a,b,c;
}a[N];
inline bool c1(node a, node b) {return a.a < b.a;}
inline bool c2(node a, node b) {return a.b < b.b;}
inline bool c3(node a, node b) {return a.c < b.c;}
void scan()
{
    scanf("%d",&n);
    fo(i,1,n)a[i].i=i,scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c);  

    sort(a+1,a+n+1,c1);fo(i,1,n)a[i].a=i;

    sort(a+1,a+n+1,c2);fo(i,1,n)a[i].b=i;

    sort(a+1,a+n+1,c3);fo(i,1,n)a[i].c=i; 
}

int c[N],d[N],pl,pr,px,cnt,rt,tot,tov[M],next[M],last[D];
struct tree
{
    int l,r;
}t[D];
inline void link(int x,int y)
{
    tov[++tot]=y;
    next[tot]=last[x];
    last[x]=tot;
}
void query(int v,int x,int y) 
{
    if(y < pl || x > pr) return;
    if(!v) return;
    if(x >= pl && y <= pr) {link(px, v); return;}
    int mid = x + y >> 1;
    query(A,x,mid);query(B,mid+1,y);
}

void insert(int &v, int x, int y) 
{
    if(y < pl || x > pr) return;
    t[++cnt] = t[v];
    link(cnt, px); if(v) link(cnt, v);
    v=cnt;
    if(x == y) return;
    int mid = x + y >> 1;
    insert(A,x,mid);insert(B,mid+1,y);
}
void connect()
{
    rt=0;
    fo(i,1,n)
    {
        pl=1;pr=c[i]-1;px=d[i];
        query(rt,1,n);
        pl=pr=c[i];px=d[i];
        insert(rt,1,n);
    }
}
void work()
{
    sort(a+1,a+n+1,c1);
    fo(i,1,n)c[i]=a[i].b,d[i]=a[i].i;
    connect();

    sort(a+1,a+n+1,c2);
    fo(i,1,n)c[i]=a[i].c,d[i]=a[i].i;
    connect();

    sort(a+1,a+n+1,c3);
    fo(i,1,n)c[i]=a[i].a,d[i]=a[i].i;
    connect();
}

int dfn[M],low[M],tim,sta[D],top,dt,fa[D],cur[D],ans[N],as;
bitset<D> vis;
void tarjan(int x)
{
    int i,y;
    d[dt=1]=x;
    while(dt)
    {
        x=d[dt];
        if(!dfn[x])
        {
            dfn[x]=low[x]=++tim;sta[++top]=x;vis[x]=1;
            cur[x]=last[x];
        }
        for(i=cur[x];i;i=next[i])
        {
            if(!dfn[y=tov[i]])
            {
                d[++dt]=y;
                fa[y]=x;
                cur[x]=next[i];
                break;
            }
            if(vis[y])low[x]=min(low[x],dfn[y]);
        }
        if(d[dt]!=x)continue;
        if(dfn[x]==low[x])
        {
            as=0;
            do
            {
                vis[y=sta[top--]]=0;
                if(y<=n)ans[++as]=y; 
            }while(x!=y);
        }
        if(fa[x])low[fa[x]]=min(low[fa[x]],low[x]);
        dt--;
    }
}

void print()
{
    sort(ans+1,ans+as+1);
    fo(i,1,as)printf("%d\n",ans[i]);    
}

int main()
{
    freopen("champion.in","r",stdin);
    freopen("champion.out","w",stdout);

    scan();

    cnt=n;
    work();

    fo(i,1,n)if(!dfn[i])tarjan(i);

    print();
}

猜你喜欢

转载自blog.csdn.net/qq_36551189/article/details/80368482