题目:排序(二分&线段树)
Posted loney-s
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题目:排序(二分&线段树)相关的知识,希望对你有一定的参考价值。
题目
思路
此题两个思路
卡着过
表面上看这是一道紫题,但是数据真的水
用心造题,用脚造数据
实际上桶排就能过
但是需要加一个小优化
对于每次排序的区间
只需要从区间的最小值枚举到最大值就行了
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
void read(int &x)
{
x=0;
int f=1;
char c=getchar();
while('0'>c||c>'9')
{
if(c=='-')
f=-1;
c=getchar();
}
while('0'<=c&&c<='9')
{
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
x*=f;
}
void write(int x)
{
if(x<10)
putchar(x+'0');
else
{
write(x/10);
putchar(x%10+'0');
}
}
int n,m;
int minn;
int maxx;
int a[100005];
int cm;
int be;
int ne;
int q;
bool t[100005];
inline void sortt(int l,int r,int f)
{
minn=INT_MAX;
maxx=INT_MIN;
for(int i=l;i<=r;i++)
{
t[a[i]]=1;
minn=min(minn,a[i]);
maxx=max(maxx,a[i]);
}
if(f==0)
{
for(int i=minn;i<=maxx;i++)
{
if(t[i])
{
a[l++]=i;
t[i]=0;
}
}
}
else
{
for(int i=maxx;i>=minn;i--)
if(t[i])
{
a[l++]=i;
t[i]=0;
}
}
}
int main()
{
read(n);
read(m);
for(int i=1;i<=n;i++)
read(a[i]);
for(int i=1;i<=m;i++)
{
read(cm);
read(be);
read(ne);
sortt(be,ne,cm);
/*for(int j=1;j<=n;j++)
{
cout<<a[j]<<' ';
}
cout<<'
';*/
}
read(q);
write(a[q]);
return 0;
}
正解
题目上有一个很有意思的点,也是本题的突破口
我们只需要输出(a_q)就行了
将序列重新构造成为一个01序列
如果(a_i<x)
则(b_i==0)
否则(b_i==1)
这样子序列的排列就很简单了
如果是从小到大,就直接000011111
或者就11100000
之后我们查(a_q)的值,如果(a_q<x)则(ans<x)
反之则(ans>x)
也就是我们可以将最后的答案通过二分确定在一个范围之内
代码(直接贴trymyedge发的STD)
#include <cstdio>
#include <cstring>
#include <cctype>
#define lc o << 1
#define rc o << 1 | 1
#define mid (l + r) / 2
using namespace std;
const int N = 100010;
int n, m, p;
int T[4 * N], lazy[4 * N]; // segment tree
int a[N], ch[N], L[N], R[N]; // the information by reading
inline int read() {
char ch = getchar();
int x = 0;
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
inline void build(int o, int l, int r, int x) {
if (l == r) {
T[o] = a[l] >= x;
lazy[o] = 0;
return;
}
build(lc, l, mid, x);
build(rc, mid + 1, r, x);
T[o] = T[lc] + T[rc];
lazy[o] = 0;
}
inline void pushdown(int o, int l, int r) {
if (!lazy[o])
return;
lazy[lc] = lazy[rc] = lazy[o];
if (lazy[o] == 1) {
T[lc] = mid - l + 1;
T[rc] = r - mid;
} else
T[lc] = T[rc] = 0;
lazy[o] = 0;
}
inline int query(int o, int l, int r, int x, int y) {
if (x <= l && y >= r)
return T[o];
if (x > r || y < l)
return 0;
pushdown(o, l, r);
return query(lc, l, mid, x, y) + query(rc, mid + 1, r, x, y);
}
inline int queryPoint(int o, int l, int r, int x) {
if (l == x && r == x)
return T[o];
pushdown(o, l, r);
if (x <= mid)
return queryPoint(lc, l, mid, x);
else
return queryPoint(rc, mid + 1, r, x);
}
inline void update(int o, int l, int r, int x, int y, int val) {
if (x <= l && y >= r) {
T[o] = val * (r - l + 1);
lazy[o] = val ? 1 : -1;
return;
}
if (x > r || y < l)
return;
pushdown(o, l, r);
update(lc, l, mid, x, y, val);
update(rc, mid + 1, r, x, y, val);
T[o] = T[lc] + T[rc];
}
inline bool check(int x) {
build(1, 1, n, x);
for (int i = 1; i <= m; i++) {
int cnt1 = query(1, 1, n, L[i], R[i]);
if (ch[i] == 0) {
update(1, 1, n, R[i] - cnt1 + 1, R[i], 1);
update(1, 1, n, L[i], R[i] - cnt1, 0);
} else {
update(1, 1, n, L[i], L[i] + cnt1 - 1, 1);
update(1, 1, n, L[i] + cnt1, R[i], 0);
}
}
return queryPoint(1, 1, n, p);
}
int main() {
n = read();
m = read();
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= m; i++) {
ch[i] = read();
L[i] = read();
R[i] = read();
}
p = read();
int ll = 1, rr = n, midd, ans;
while (ll <= rr) {
midd = (ll + rr) >> 1;
if (check(midd))
ans = midd, ll = midd + 1;
else
rr = midd - 1;
}
printf("%d
", rr);
return 0;
}
以上是关于题目:排序(二分&线段树)的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 4552 [Tjoi2016&Heoi2016]排序 (二分答案 线段树)
bzoj4552[Tjoi2016&Heoi2016]排序 二分+线段树
bzoj4552/Tjoi2016&Heoi2016排序——二分+线段树/平衡树+线段树分裂与合并
bzoj4552: [Tjoi2016&Heoi2016]排序(二分+线段树)