【描述】
cjBBteam拥有一个很大的野生动物园。这个动物园坐落在一个狭长的山谷内,这个区域从南到北被划分成N个区域,每个区域都饲养着一头狮子。这些狮子从北到南编号为1,2,3,…,N。每头狮子都有一个觅食能力值Ai,Ai越小觅食能力越强。饲养员cmdButtons决定对狮子进行M次投喂,每次投喂都选择一个区间[I,J],从中选取觅食能力值第K强的狮子进行投喂。值得注意的是,cmdButtons不愿意对某些区域进行过多的投喂,他认为这样有悖公平。因此cmdButtons的投喂区间是互不包含的。你的任务就是算出每次投喂后,食物被哪头狮子吃掉了。
【输入格式】
输入第一行有两个数N和M。此后一行有N个数,从南到北描述狮子的觅食能力值。此后M行,每行描述一次投喂。第t+2的三个数I,J,K表示在第t次投喂中,cmdButtons选择了区间[I,J]内觅食能力值第K强的狮子进行投喂。
【输出格式】
输出有M行,每行一个整数。第i行的整数表示在第i次投喂中吃到食物的狮子的觅食能力值。
【样例输入】
7 2
1 5 2 6 3 7 4
1 5 3
2 7 1
【样例输出】
3
2
【分析】
平衡树解法:
由题目给出的区间互相不包含可以得出,若将每次询问的区间按照起始区域进行排序,那一定是一段接一段,只有可能是两种情况:
下一段的左端与上一段的右端不相交或者相交。
这两种情况都是前面的数据与后面的数据互不影响,因此将区间排序之后,对于每一个区间,删除掉前面多余的,插入后面不够的,使平衡树中仅留下该区间中的数据,然后直接找第k小即可。
SBT可解。
1 /* ***********************************************
2 MYID : Chen Fan
3 LANG : G++
4 PROG : VIJOS1081
5 ************************************************ */
6
7 #include <iostream>
8 #include <cstdio>
9 #include <cstring>
10 #include <algorithm>
11
12 using namespace std;
13
14 #define MAXN 100010
15
16 int sons[MAXN][2];
17 int size[MAXN],data[MAXN];
18 int sbt=0,sbttail=0;
19
20 void rotate(int &t,int w) //rotate(&node,0/1)
21 {
22 int k=sons[t][1-w];
23 if (!k) return ;
24 sons[t][1-w]=sons[k][w];
25 sons[k][w]=t;
26 size[k]=size[t];
27 size[t]=size[sons[t][0]]+size[sons[t][1]]+1;
28 t=k;
29 }
30
31 void maintain(int& t,bool flag) //maintain(&node,flag)
32 {
33 if (!t) return ;
34 if (!flag)
35 if (size[sons[sons[t][0]][0]]>size[sons[t][1]]) rotate(t,1);
36 else if (size[sons[sons[t][0]][1]]>size[sons[t][1]])
37 {
38 rotate(sons[t][0],0);
39 rotate(t,1);
40 } else return ;
41 else
42 if (size[sons[sons[t][1]][1]]>size[sons[t][0]]) rotate(t,0);
43 else if (size[sons[sons[t][1]][0]]>size[sons[t][0]])
44 {
45 rotate(sons[t][1],1);
46 rotate(t,0);
47 } else return ;
48
49 maintain(sons[t][0],false);
50 maintain(sons[t][1],true);
51 maintain(t,false);
52 maintain(t,true);
53 }
54
55 void insert(int& t,int v) //insert(&root,0,value)
56 {
57 if (!t)
58 {
59 sbttail++;
60 data[sbttail]=v;
61 size[sbttail]=1;
62 sons[sbttail][0]=0;
63 sons[sbttail][1]=0;
64 t=sbttail;
65 } else
66 {
67 size[t]++;
68 if (v<data[t]) insert(sons[t][0],v);
69 else insert(sons[t][1],v);
70 maintain(t,v>=data[t]);
71 }
72 }
73
74 int del(int& t,int v) //del(&root,key)
75 {
76 size[t]--;
77 if (v==data[t]||(v<data[t]&&sons[t][0]==0)||(v>data[t]&&sons[t][1]==0))
78 {
79 int ret=data[t];
80 if (sons[t][0]==0||sons[t][1]==0) t=sons[t][1]+sons[t][0];
81 else data[t]=del(sons[t][0],data[t]+1);
82 return ret;
83 } else
84 if (v<data[t]) return del(sons[t][0],v);
85 else return del(sons[t][1],v);
86 }
87
88 int select(int t,int k)
89 {
90 if (k==size[sons[t][0]]+1) return t;
91 if (k<=size[sons[t][0]]) return select(sons[t][0],k);
92 else return select(sons[t][1],k-1-size[sons[t][0]]);
93 }
94
95 typedef struct nod
96 {
97 int i,l,r,k;
98 } node;
99 node d[50010];
100
101 bool op(node a,node b)
102 {
103 if (a.l==b.l) return a.r<b.r;
104 else return a.l<b.l;
105 }
106
107 int a[MAXN];
108
109 typedef struct nod1
110 {
111 int i,ans;
112 } node1;
113 node1 out[50010];
114
115 bool op1(node1 a,node1 b)
116 {
117 return a.i<b.i;
118 }
119
120 int main()
121 {
122 freopen("1.txt","r",stdin);
123
124 sbt=0,sbttail=0;
125 int n,m;
126 scanf("%d%d",&n,&m);
127 for (int i=1;i<=n;i++) scanf("%d",&a[i]);
128 for (int i=1;i<=m;i++)
129 {
130 scanf("%d%d%d",&d[i].l,&d[i].r,&d[i].k);
131 d[i].i=i;
132 }
133 sort(&d[1],&d[m+1],op);
134
135 int l=0,r=0;
136 for (int i=1;i<=m;i++)
137 {
138 if (r<d[i].l)
139 {
140 sbt=0;
141 sbttail=0;
142 for (int j=d[i].l;j<=d[i].r;j++) insert(sbt,a[j]);
143 } else
144 {
145 for (int j=l;j<d[i].l;j++) del(sbt,a[j]);
146 for (int j=r+1;j<=d[i].r;j++) insert(sbt,a[j]);
147 }
148 l=d[i].l;
149 r=d[i].r;
150 int temp=select(sbt,d[i].k);
151
152 out[i].i=d[i].i;
153 out[i].ans=data[temp];
154 }
155
156 sort(&out[1],&out[m+1],op1);
157 for (int i=1;i<=m;i++) printf("%d\n",out[i].ans);
158
159 return 0;
160 }
View Code
划分树解法:
划分树是一种类似快排的数据结构,可以快速在O(logn)的时间内直接求出某个区间内的k值。
然后本题就是......一棵裸的划分树,直接套即可
。。。。。。最后的结果是,不知道为什么比SBT要慢很多,直观的感觉上划分树没有多余的删除操作,应该会快很多的
1 /* ***********************************************
2 MYID : Chen Fan
3 LANG : G++
4 PROG : VIJOS1081_SortTree
5 ************************************************ */
6
7 #include <iostream>
8 #include <cstdio>
9 #include <cstring>
10 #include <algorithm>
11
12 using namespace std;
13
14 #define MAXN 100010
15
16 int a[MAXN],dp[20][MAXN],tree[20][MAXN];
17
18 void maketree(int c,int l,int r)
19 {
20 int mid=(l+r)/2,ls=l,rs=mid+1,num=0;
21
22 for (int i=mid;i>=l&&a[i]==a[mid];i--) num++;
23 for (int i=l;i<=r;i++)
24 {
25 if (i==l) dp[c][i]=0;
26 else dp[c][i]=dp[c][i-1];
27
28 if (tree[c][i]<a[mid])
29 {
30 dp[c][i]++;
31 tree[c+1][ls]=tree[c][i];
32 ls++;
33 } else
34 if (tree[c][i]>a[mid])
35 {
36 tree[c+1][rs]=tree[c][i];
37 rs++;
38 } else
39 {
40 if (num)
41 {
42 num--;
43 dp[c][i]++;
44 tree[c+1][ls]=tree[c][i];
45 ls++;
46 } else
47 {
48 tree[c+1][rs]=tree[c][i];
49 rs++;
50 }
51 }
52 }
53
54 if (l==r) return ;
55 maketree(c+1,l,mid);
56 maketree(c+1,mid+1,r);
57 }
58
59 int query(int c,int l,int r,int ql,int qr,int k)
60 {
61 if (l==r) return tree[c][l];
62 int s,ss,mid=(l+r)/2;
63
64 if (l==ql)
65 {
66 s=0;
67 ss=dp[c][qr];
68 } else
69 {
70 s=dp[c][ql-1];
71 ss=dp[c][qr]-s;
72 }
73 if (k<=ss) return query(c+1,l,mid,l+s,l+s+ss-1,k);
74 else return query(c+1,mid+1,r,mid-l+1+ql-s,mid-l+1+qr-s-ss,k-ss);
75 }
76
77 int main()
78 {
79 //freopen("1.in","r",stdin);
80 //freopen("zoo8.in","r",stdin);
81 //freopen("1.out","w",stdout);
82
83 int n,m;
84 scanf("%d%d",&n,&m);
85
86 for (int i=1;i<=n;i++)
87 {
88 scanf("%d",&a[i]);
89 tree[0][i]=a[i];
90 }
91 sort(&a[1],&a[n+1]);
92
93 maketree(0,1,n);
94
95 for (int i=1;i<=m;i++)
96 {
97 int l,r,k;
98 scanf("%d%d%d",&l,&r,&k);
99 printf("%d\n",query(0,1,n,l,r,k));
100 }
101
102 return 0;
103 }
View Code
Do Cool Things That Matter!