【HDU5649 BestCoder Round 76 (div1)D】【二分+線段樹】DZY Loves Sorting 全排列1~n 區間升序降序排序 最後k位置的數是幾
阿新 • • 發佈:2019-02-20
DZY Loves Sorting
Accepts: 6 Submissions: 8 Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others) 問題描述DZY有一個數列a[1..n],它是1∼n這n個正整數的一個排列。 現在他想支援兩種操作: 0lr: 將a[l..r]原地升序排序。 1lr: 將a[l..r]原地降序排序。 操作完後,他會給你指定一個位置k,請你告訴他輸入描述a[k]的值。
第一行t,表示有t組資料。 接下來t組資料。 每組資料中: 第一行有兩個整數n,m,其中m表示運算元目。 第二行是空格隔開的n個正整數a[1],a[2],⋯,a[n],表示陣列的初始值,保證它是1∼n的一個排列。 接下來m行,每行有三個整數opt,l,r,表示一次操作。 最後一行為整數k。 (1≤t≤50,1≤n,m≤100000,輸出描述1≤k≤n,1≤l≤r≤n,opt∈{0,1},所有資料的n之和不超過150000,所有資料的m之和不超過150000)
每組資料輸出一行答案,表示操作完後a[k]的值。
輸入樣例
1 6 3 1 6 2 5 3 4 0 1 4 1 3 6 0 2 4 3輸出樣例
5Hint
1 6 2 5 3 4 -> [1 2 5 6] 3 4 -> 1 2 [6 5 4 3] -> 1 [2 5 6] 4 3,最終a[3]=5。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define lson o<<1,l,mid #define rson o<<1|1,mid+1,r #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; } const int N = 2e5+10, Z = 1e9 + 7, ms63 = 0x3f3f3f3f; int casenum, casei; int n, m, k; int a[N]; int op[N], l[N], r[N]; int M, L, R, V; int sum[1 << 19]; int len[1 << 19]; int flag[1 << 19]; void pushup(int o) { sum[o] = sum[ls] + sum[rs]; } void pushdown(int o) { if (~flag[o]) { sum[ls] = len[ls] * flag[o]; sum[rs] = len[rs] * flag[o]; flag[ls] = flag[o]; flag[rs] = flag[o]; flag[o] = -1; } } void build(int o, int l, int r) { len[o] = r - l + 1; flag[o] = -1; if (l == r) { sum[o] = (a[l] > M); return; } int mid = (l + r) >> 1; build(lson); build(rson); pushup(o); } int check(int o, int l, int r) { if (L <= l&&R >= r)return sum[o]; pushdown(o); int ret = 0; int mid = (l + r) >> 1; if (L <= mid)ret += check(lson); if (R > mid)ret += check(rson); return ret; } void change(int o, int l, int r) { if (L <= l&&R >= r) { sum[o] = len[o] * V; flag[o] = V; return; } pushdown(o); int mid = (l + r) >> 1; if (L <= mid)change(lson); if (R > mid)change(rson); pushup(o); } int num[2]; int main() { scanf("%d", &casenum); for (casei = 1; casei <= casenum; ++casei) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i)scanf("%d", &a[i]); for (int i = 1; i <= m; ++i)scanf("%d%d%d", &op[i], &l[i], &r[i]); scanf("%d", &k); int lft = 1; int rgt = n; while (lft < rgt) { M = (lft + rgt) >> 1; build(1, 1, n); for (int i = 1; i <= m; ++i) { L = l[i]; R = r[i]; num[1] = check(1, 1, n); num[0] = R - L + 1 - num[1]; V = op[i]; L = l[i]; R = L + num[V] - 1; if (L <= R)change(1, 1, n); V = V ^ 1; L = R + 1; R = r[i]; if (L <= R)change(1, 1, n); } L = k; R = k; int val = check(1, 1, n); val == 1 ? lft = M + 1 : rgt = M; } printf("%d\n", lft); } return 0; } /* 【trick&&吐槽】 不要忘記區間操作要保證L<=R哦 【題意】 給你一個a[],a[]是[1,n]的一個全排列。 我們有兩種操作。 0 l r,把[l,r]排序升序 1 l r,把[l,r]排序降序 當做完全部的操作之後,我們告訴你一個位置k,讓你求ans[k], 即第k個位置的數的數值是多少。 【型別】 線段樹+二分 【分析】 這道題一個很特殊的地方,就是我們的查詢只有一個ans[k] ans[k]對應著a[]呢? 因為是全排列。於是,我們存在一種二分答案的可能解法。 我們可以二分ans[k]對應著數mid 這時,把<=mid的數標記為0,把>mid的數標記為1。 然後我們用線段樹模擬全部的操作。 這裡只要實現—— 1,查詢區間中1的個數 2,區間賦值 對於升序排序或者逆序排序,我們查詢1的個數為num後。 升序就是把後num個數賦值為1,降序則把前num個數賦值為1。 這樣操作到最後,我們查詢ans[k]。 如果對應的ans[k]為1,則說明當前的數是[mid,r]範圍的數; 否則當前的數是(l,mid)範圍的數。 於是到最後就可以得到答案 【時間複雜度&&優化】 O(nlogn) */