【XSY2892】【GDSOI2018】谁是冠军
题目来源:noi2018模拟测试赛(二十三)T3 san
为什么noi模拟赛里会做到省选原题啊……
题意:
Description
有n个人,简单起见把他们编号为1到n,每个人有三项指标分别是攻击力,防御力和血量。现在要进行n-1场比赛,每次在剩下的人里面随机地选出两个人进行比赛,当一个人有大于等于两项指标严格大于另一个人时,这个人获得胜利。每次比赛,胜者留下,败者淘汰。
现在请你求出所有可能成为冠军(留到最后)的人。Input
第1行有一个整数n,表示人数为n。
接下来n行,每行有三个整数$a_i,b_i,c_i$分别表示编号为i的人的攻击力,防御力和血量。
数据保证对于任意两个人的同一项指标值不会相同。Output
按编号从小到大输出有可能成为冠军的人的编号,每行输出一个数。Hint
对于$60\%$的数据$1\leq n\leq 5000$
对于$100\%$的数据$1\leq n\leq 100000$
题解:
首先60分很简单,暴力建图,即如果x能胜y就从x到y建有向边,最后从每个点出发看能否遍历整个图就好了;
也可以tarjan缩点之后直接找入度为0的联通块,块内的点就是答案;
但这样做依然是$O(n^2)$的,复杂度瓶颈在于边数是$O(n^2)$的,因此很容易想到线段树优化建图;
由于他的要求是至少两维小于,所以可以两两拆开来做三遍;
那么先把第一维排序,第二维离散化后开一颗权值线段树,新加入一个点i就将它连向权值在$[1,i的第二维权值-1]$内的区间;
这样子做一个点最多只会连向$O(logn)$个区间,所以能过;
注意修改具有时效性,所以要边加边连,具体见代码。
代码:
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #include<queue>
7 #include<stack>
8 #define inf 2147483647
9 #define eps 1e-9
10 using namespace std;
11 typedef long long ll;
12 typedef double db;
13 struct edge{
14 int v,next;
15 }a[20000001];
16 struct uni{
17 int a,b,c,id;
18 }p[100001];
19 struct node{
20 int ls,rs,v;
21 }t[500001];
22 int n,tot=0,tid=0,bcc=0,tim=0,cnt,rt,dfn[6000001],low[6000001],blg[6000001],head[6000001];
23 int ans[100001],anss=0;
24 stack<int>s;
25 bool cmp(uni a,uni b){
26 return a.a<b.a;
27 }
28 bool _cmp(uni a,uni b){
29 return a.b<b.b;
30 }
31 void add(int u,int v){
32 //printf("Added %d %d\n",u,v);
33 a[++tot].v=v;
34 a[tot].next=head[u];
35 head[u]=tot;
36 }
37 void build(int &u,int l,int r){
38 u=++cnt;
39 t[u].v=0;
40 if(l==r)return;
41 int mid=(l+r)/2;
42 build(t[u].ls,l,mid);
43 build(t[u].rs,mid+1,r);
44 }
45 void updata(int u,int l,int r,int p,int x){
46 if(l==r){
47 t[u].v=x;
48 return;
49 }
50 int mid=(l+r)/2;
51 if(p<=mid)updata(t[u].ls,l,mid,p,x);
52 else updata(t[u].rs,mid+1,r,p,x);
53 if(t[t[u].ls].v&&t[t[u].rs].v){
54 t[u].v=++tid;
55 add(tid,t[t[u].ls].v);
56 add(tid,t[t[u].rs].v);
57 }else t[u].v=t[t[u].ls].v+t[t[u].rs].v;
58 }
59 void query(int u,int l,int r,int L,int R,int id){
60 if(!t[u].v)return;
61 if(l==L&&r==R){
62 add(id,t[u].v);
63 return;
64 }
65 int mid=(l+r)/2;
66 if(L<=mid)query(t[u].ls,l,mid,L,min(mid,R),id);
67 if(mid<R)query(t[u].rs,mid+1,r,max(mid+1,L),R,id);
68 }
69 void link(){
70 sort(p+1,p+n+1,cmp);
71 cnt=0;
72 build(rt,1,n);
73 //puts("----12----");
74 for(int i=1;i<=n;i++){
75 query(rt,1,n,1,p[i].b-1,p[i].id);
76 updata(rt,1,n,p[i].b,p[i].id);
77 }
78 cnt=0;
79 build(rt,1,n);
80 //puts("----13----");
81 for(int i=1;i<=n;i++){
82 query(rt,1,n,1,p[i].c-1,p[i].id);
83 updata(rt,1,n,p[i].c,p[i].id);
84 }
85 sort(p+1,p+n+1,_cmp);
86 cnt=0;
87 build(rt,1,n);
88 //puts("----23----");
89 for(int i=1;i<=n;i++){
90 query(rt,1,n,1,p[i].c-1,p[i].id);
91 updata(rt,1,n,p[i].c,p[i].id);
92 }
93 }
94 void tarjan(int u){
95 dfn[u]=low[u]=++tim;
96 s.push(u);
97 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
98 int v=a[tmp].v;
99 if(!dfn[v]){
100 tarjan(v);
101 low[u]=min(low[u],low[v]);
102 }else if(!blg[v])low[u]=min(low[u],dfn[v]);
103 }
104 if(low[u]==dfn[u]){
105 blg[u]=++bcc;
106 int nw=s.top();
107 while(nw!=u){
108 nw=s.top();
109 blg[nw]=bcc;
110 s.pop();
111 }
112 }
113 }
114 int main(){
115 memset(head,-1,sizeof(head));
116 scanf("%d",&n);
117 for(int i=1;i<=n;i++){
118 scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c);
119 p[i].id=i;
120 }
121 tid=n;
122 link();
123 for(int i=1;i<=n;i++){
124 if(!dfn[i]){
125 tarjan(i);
126 }
127 }
128 for(int i=1;i<=n;i++){
129 if(blg[i]==bcc)ans[++anss]=i;
130 }
131 for(int i=1;i<=anss;i++){
132 printf("%d\n",ans[i]);
133 }
134 return 0;
135 }