Problem
Input
Output
Hint
Solution
60points:暴力 or tarjan
首先,考虑转换模型。对于两个人x,y,如果x能胜y,则从x向y连一条边;反之则反。那么原题就转化为了给出一个竞赛图,求能够遍历到所有点的点。
可以枚举每个点x,从x开始遍历,判断能否可以遍历到所有点。这样暴力的复杂度为
O(n2)
O
(
n
2
)
。
当然,我们也可以tarjan一下,然后取出拓扑序最前的分量。注意,这只会取出一个分量。因为根据竞赛图的性质,缩完了点之后,任意两个分量间依然会有至少一条边。这样做的复杂度为
O(n(点数)+n2(边数))
O
(
n
(
点
数
)
+
n
2
(
边
数
)
)
。
时间复杂度:
O(n2)
O
(
n
2
)
。
100points:主席树优化连边
上述做法的瓶颈在于连边。因为我们始终要连
O(n2)
O
(
n
2
)
条边,所以时间复杂度始终不能少于边数。
那么其实我们可以用主席树优化连边。
不要看到“主席树”这三个字就怕了,因为本题不须打树套树,所以应该也不算主席树;况且打起来也非常简单。不过如果你一点都不会还是戳一戳这里吧。
首先,由于是两项指标都大于就可以造成连边,所以原题可以转化为a,b的贡献,b,c的贡献以及c,a的贡献。
我们先考虑a,b两项指标造成的连边。
首先,按a将所有人从小到大排序。然后顺序扫一遍。每扫到一个人i,我们就从点i连向权值线段树中[1,bi-1]这个区间内所有的完整区间(可以分成
log2n
l
o
g
2
n
个完整区间)连边(线段树中每一个点代表一个区间);然后从所有包含bi的区间向i连边(至多有
log2n
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(nlog2n)
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();
}