hdu 5737【线段树+有序表+线段树小技巧+ 二分不要写错……】
来源:互联网 发布:ubuntu启动脚本编写 编辑:程序博客网 时间:2024/06/05 05:53
题目意思:
必须在线算法, 给A数组n个元素,B数组n个元素。
问,[L,R]区间,A数组在[L,R]内,有几个数字>=B数组在[L,R]范围内的元素。 并且这个是ai>=bi, i是相同的。
比如[4,7], 答案就是 (a4>=b4 ) + (a5>=b5) + (a6>=b6) + (a7>=b7) 【为bool运算,结果返回0,1……】
算法1:
用A数组构建线段树,然后在线段树每一个线段中保存一个东西。
比如线段树的[L,R]区间,还保存B数组排序好的元素。
举个例子:
B数组为:5 4 1 7 2
[1,5]区间内, 还保存了一个1 2 4 5 7的有序数组
在[1,3]内,保存一个[1, 4,5]的有序数组。
有序数组可以用归并排序快速得到(在构建线段树的时候就顺便得到了)。
然后线段树的其他信息,大致就是常规的线段树信息了。
然后对于修改一个区间[L,R],找到线段树区间[L,R],然后因为[L,R]都改为同一个数字,我们想知道[L,R]的答案,只需要在对应的有序数组里二分一下就可以得到答案了。
这个算法,是nlog^2n。 据说被卡了……
其实我们不需要维护那么多东西,记录一些额外信息就行了。对于一个区间[L,R],如果被全部赋值为一个数字的话,我希望知道这个数字,在这个【排序后】的区间里,可以排第几。
有了这样的想法,我们可以预处理出一些东西。
在[L,R]排第k名的,在[L,MID]可以排第几名?在[MID+1,R]可以排第几名?这样整个题就做完了
显然,在[1,n]中,可以二分查找出所要的结果,假设为P名。
之后要做的就是往左右子树种传递真个P(显然在左右子树种,P会变~)。
然后要考虑一个情况,就是修改的区间的数字X,比[1,n]最大的数字都大的情况怎么办,程序写的好一点就行了……
#include <iostream>#include <ctime>#include <cstdio>#include <queue>#include <cstdlib>#include <algorithm>#include <cstring>#include <vector>#include <map>#include <string>using namespace std;typedef long long LL;const int maxn = 10e5 *4 + 10;const LL mod = 1e9 + 7;int a[maxn], b[maxn];int n, m;int ans[maxn], paixu[maxn], tmp_array[maxn];int st[20][maxn], lk[20][maxn], rk[20][maxn];//排序后的数组,每一层的若干个位置int change[maxn];namespace getnum{ int a , b , C = ~(1<<31), M = (1<<16)-1; inline int rnd(int last) { a = (36969 + (last >> 3)) * (a & M) + (a >> 16); b = (18000 + (last >> 3)) * (b & M) + (b >> 16); return (C & ((a << 16) + b)) % 1000000000; //return (C & ((a << 16) + b)) % 10; }};using getnum::rnd;void build(int o, int L, int R,int deep){ //change[o] = -1; if (L == R) { ans[o] = a[L] >= b[L]; st[deep][L] = b[L]; return ; } int M = L + (R -L)/2; int lc = o * 2, rc = o * 2 + 1; build(lc, L, M, deep + 1); build(rc, M + 1, R, deep + 1); ans[o] = ans[lc] + ans[rc]; lc=L, rc= M + 1; for (int i = L; i <= R; ++ i) //归并排序 if (rc > R || lc <= M & st[deep + 1][lc] <= st[deep + 1][rc]) { st[deep][i] = st[deep + 1][lc++]; } else { st[deep][i] = st[deep + 1][rc++]; } lc = L, rc = M + 1; for (int i = L; i <= R; ++ i) //计算每个数字在左右儿子中的情况 { while (st[deep][i] > st[deep + 1][lc] && lc <= M) ++lc; lk[deep][i] = lc; while (st[deep][i] > st[deep + 1][rc] && rc <= R) ++ rc; rk[deep][i] = rc; } //cout<<o<<" " <<ans[o]<<"!!"<<endl; //lk[deep][R+1] = M +1; //rk[deep][R+1] = R + 1;}int ql, qr, v;inline void push_down(int o, int L, int R, int deep){ if (change[o] != -1 && L < R) { int lc = o * 2, rc = o * 2 + 1, M = L + (R-L)/2; change[lc] = change[o] > R ? M + 1 : lk[deep][change[o]]; change[rc] = change[o] > R ? R + 1 : rk[deep][change[o]]; change[o] = -1; }}inline void maintain(int o, int L, int R){ int lc = o * 2, rc = o * 2 + 1; if (change[o] != -1) ans[o] = change[o] - L; else if (L < R) ans[o] = ans[lc] + ans[rc];}int query(int o, int L, int R,int deep) //询问ql,qr区间{ //cout<<o<<" " <<L<<" " <<R <<" " <<deep<<" " <<change[o]<< " "<<ans[o]<<endl; if (change[o] != -1) //有标记的情况下,输出标记信息 { //有些题可以直接根据段标记知道答案 //就可以直接return了 //return change[o] - L; } if (ql <=L && R <= qr) { if (change[o] != -1) ans[o] = change[o] - L; return ans[o]; } push_down(o, L, R, deep); int M = L + (R - L)/2, lc = o * 2, rc = o * 2 + 1; int ret = 0; if (ql <= M) ret += query(lc, L, M, deep + 1); else maintain(lc, L, M); if (qr > M) ret += query(rc, M + 1, R, deep + 1); else maintain(rc, M+1, R); maintain(o, L, R); return ret;}void update(int o, int L, int R, int deep, int flag) //给ql,qr区间,都变为v,这些都是全局变量{// cout<<o<<" " <<L<<" " <<R <<" " <<deep<<" " <<change[o]<< " "<<ans[o]<<" "<<flag<<endl; if (ql <= L && R <= qr) { change[o] = flag; ans[o] = flag - L; return; } else { push_down(o, L, R, deep); int M = L + (R - L)/2, lc = o * 2, rc = o * 2 + 1; if (ql <= M) update(lc, L, M, deep + 1, flag >R ? M + 1 : lk[deep][flag]); else maintain(lc, L, M); if (qr > M) update(rc, M + 1, R, deep + 1, flag > R ? R + 1 : rk[deep][flag]); else maintain(rc, M + 1, R); } maintain(o, L, R);}inline void UD(int L, int R, int x){ //L,R区间都改为X int l = 0, r = n + 1, m; st[1][n + 1]=0x7fffffff; while (l + 1 < r) //(l,r]区间找比x大的最小值 { m = l + (r-l)/2; if (st[1][m]<=x) l = m; else r = m; } update(1, 1, n, 1, r);}void init(){ scanf("%d%d%d%d", &n, &m, &getnum::a, &getnum::b); for (int i = 1; i <= n; ++ i) scanf("%d", &a[i]); for (int i = 1; i <= n; ++ i) scanf("%d", &b[i]); build(1,1,n,1);}void doit(){ int last =0; LL ans = 0; memset(change , -1, sizeof(change)); for (int i = 1; i <= m; ++ i) { int l = rnd(last) % n + 1; int r = rnd(last) % n + 1; int x = rnd(last) + 1; if (l>r) swap(l,r); ql = l, qr = r; if ((l+r+x)%2==0) { //询问 last = query(1, 1, n, 1); ans = (ans + 1LL * i * (LL)last) % mod; }else { //修改 UD(l, r, x); } } printf("%lld\n", ans);}int main(){ int T; scanf("%d", &T); while (T--) { init(); doit(); } return 0;}
- hdu 5737【线段树+有序表+线段树小技巧+ 二分不要写错……】
- hdu 4339 线段树+二分
- hdu 4614 线段树+二分~
- HDU 4614 线段树+二分
- HDU 4791二分+线段树
- HDU - 4973(线段树+二分)
- hdu 4339 线段树+二分
- hdu 4614 线段树+二分
- hdu 5592 线段树 + 二分
- HDU 3450 线段树+二分
- hdu 4614 线段树+二分
- HDU 5649 (二分 线段树)
- HDU 6070 二分+线段树
- hdu 6070二分+线段树
- HDU 4614 线段树+二分
- HDU 5649 线段树+二分
- hdu 4737 线段树维护+二分
- hdu - 4747 - Mex(二分+线段树)
- hdu 5806 NanoApe Loves Sequence Ⅱ 前缀和+尺取法
- (小案例)数组应用:实现学生管理系统
- java中String、List、set的一些常用方法
- sql排序问题
- C#中Cookie的概述及应用
- hdu 5737【线段树+有序表+线段树小技巧+ 二分不要写错……】
- DojoX 评分 widget 实际效果
- struts2 配置文件模板
- Geekband第十三周作业
- Combination Sum III
- 剑指offer-面试题7
- 【PAT】(乙级)1005. 继续(3n+1)猜想 (25)
- c语言实现内存逆序查找函数
- poj 3126 Prime Path