【ZJOI 2013】 K大数查询 · 续
? ? · 在线算法弱爆了... ? ? · 分治神器 ? ? · Orz小oy ? ? 小oy竟然BZOJ rank1了?! ? ? 这怎么行呢...~(≧▽≦)/~ ? ? 原来说过本题可以用树套树搞定...不过那是在线算法. ? ? 今天说说离线算法... ? ? 考虑到在线算法是在值域上做二分. ? ? 那么同样可以考虑离线二分. ? ? 定义solve(L,R),表示解决答案值域在[L,R]中的询问. ? ? 明显我们需要求的是solve(1,n) ? ? 考虑调用solve(1,n),我们可以将所有的操作分为2类: ? ? 对于1操作,如果其修改值≤ n / 2,归入类1? ? ? 对于2操作,如果其答案值域≤ n / 2,归入类1 ? ? 这样,每层我们的任务都是将当前操作分类. ? ? 对于1操作,O(1) 可分类. ? ? 对于2操作,我们可以转化为如下问题: ? ? 判断[l,r] 中≤ n / 2 的数的个数是否<k ? ? ? 其中l,r为2操作的区间,k为2操作的询问值. ? ??明显,我们已经将在该操作之前的所有修改值≤ n / 2 的1操作分类过. ? ? 只需要维护一个区间和即可. ? ? 分类完后递归询问solve(1,n / 2),solve(n / 2 + 1,?依次类推即可. ? ? 为了常数用树状数组吧. ? ? 于是复杂度O(nlog^2n),?常数小得可怕. ? ? 特别无聊把小oy压了4ms... ( 有意义么! 你这个大丧失! ) #include <cstdio> #include <cstdlib> #include <algorithm> using namespace std; int n,m,max_i,set; int vis1[50010],vis2[50010]; int right[20][50010],left[20][50010],tot1[50010],tot2[50010]; int a[50010],b[50010],c[50010],d[50010],now[50010],ans[50010]; int toT(int *q,int *p,int x) { int t = 0; for (int i = x; i; i -= i & -i) if (q[i] == set) t += p[i]; else q[i] = set,p[i] = 0; return t; } void add(int *q,int x,int w) { for (int i = x; i <= max_i; i += i & -i) if (q[i] == set) p[i] += w; else q[i] = set,p[i] = w; } void solve(int cr,int l,int r) { if (!p[0]) return; int mid = (l + r) >> 1; int *rig = right[cr],*lef = left[cr]; rig[0] = lef[0] = 0; ++set; for (int i = 1; i <= p[0]; ++i) { int I = p[i]; if (a[I] == 1) if (d[I] > mid) rig[++rig[0]] = I; else { add(vis1,tot1,b[I],1); add(vis2,tot2,b[I]); add(vis1,c[I] + 1,-1); add(vis2,-c[I] - 1); lef[++lef[0]] = I; } else { int tot = toT(vis1,c[I]) * (c[I] + 1) - toT(vis2,c[I]) - toT(vis1,b[I] - 1) * b[I] + toT(vis2,b[I] - 1); if (tot + now[I] < d[I]) now[I] += tot,ans[I] = mid,rig[++rig[0]] = I; else lef[++lef[0]] = I; } } if (l == r) return; solve(cr + 1,lef,l,mid); solve(cr + 1,rig,mid + 1,r); } int main() { scanf("%d %d",&n,&m); for (int i = 1; i <= m; ++i) { scanf("%d %d %d %d",a + i,b + i,c + i,d + i); if (a[i] == 1) d[i] = n - d[i] + 1; right[0][i] = i; } right[0][0] = m; max_i = n; solve(1,right[0],1,n); for (int i = 1; i <= m; ++i) if (a[i] == 2) printf("%dn",n - ans[i]); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |