「BZOJ3110」[ZJOI2013] K大数查询
Description
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
Input
第一行N,M
接下来M行,每行形如1 a b c或2 a b c
Output
输出每个询问的结果
Sample Input
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
1
2
1
HINT
N,M<=50000,N,M<=50000a<=b<=N
1操作中abs(c)<=N
2操作中abs(c)<=Maxlongint
题解
我写的树套树做法,第一位权值,第二位区间
写的常数太渣了。。。标记还是永久化比较好。。。
对于区间[l,r],将权值在此之内的修改建立一棵普通线段树。这样对于一个询问,就可以类似二分答案,首先看权值在[1,mid]中有几个在询问的区间中,如果<排名,就往右,否则往左。
注:本题数据没有负数TAT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #define ll long long using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int a,b,c; int n,m,sz; int root[200005]; int ls[20000005],rs[20000005],sum[20000005],lazy[20000005]; void pushdown(int k,int l,int r) { if(!lazy[k]||l==r)return; if(!ls[k])ls[k]=++sz; if(!rs[k])rs[k]=++sz; lazy[ls[k]]+=lazy[k];lazy[rs[k]]+=lazy[k]; int mid=(l+r)>>1; sum[ls[k]]+=(mid-l+1)*lazy[k]; sum[rs[k]]+=(r-mid)*lazy[k]; lazy[k]=0; } void modify(int &k,int l,int r,int a,int b) { if(!k)k=++sz; pushdown(k,l,r); if(l==a&&r==b) { sum[k]+=r-l+1; lazy[k]++; return; } int mid=(l+r)>>1; if(b<=mid)modify(ls[k],l,mid,a,b); else if(a>mid)modify(rs[k],mid+1,r,a,b); else { modify(ls[k],l,mid,a,mid);modify(rs[k],mid+1,r,mid+1,b); } sum[k]=sum[ls[k]]+sum[rs[k]]; } int query(int k,int l,int r,int a,int b) { if(!k)return 0; pushdown(k,l,r); if(l==a&&r==b)return sum[k]; int mid=(l+r)>>1; if(b<=mid)return query(ls[k],l,mid,a,b); else if(a>mid)return query(rs[k],mid+1,r,a,b); else return query(ls[k],l,mid,a,mid)+query(rs[k],mid+1,r,mid+1,b); } void insert() { int k=1,l=1,r=n; while(l!=r) { int mid=(l+r)>>1; modify(root[k],1,n,a,b); if(c<=mid)r=mid,k=k<<1; else l=mid+1,k=k<<1|1; } modify(root[k],1,n,a,b); } int solve() { int l=1,r=n,k=1; while(l!=r) { int mid=(l+r)>>1; int t=query(root[k<<1],1,n,a,b); if(t>=c)r=mid,k<<=1; else l=mid+1,k=k<<1|1,c-=t; } return l; } int main() { n=read();m=read(); while(m--) { int f=read();a=read();b=read();c=read(); if(f==1) { c=n-c+1;insert(); } else printf("%d\n",n-solve()+1); } return 0; } |
黄学长您代码 WA 了啊!!!大视野数据是有负数的,而且数字个数会爆 int!
大哥您先看看黄学长什么时候写的这题好吗……
所以要尽量做到实时更新啊。。。程序的 bug 跟日期又没有关系。。。
这么多道题
更新。。。。。。。。。。。。。。。
还实时
现在要longlong了
unsigned int也可以
orz yww
https://blog.sengxian.com/solutions/bzoj-3110
无耻的安利下,3.8 确实加了一组比较 Trick 的数据。
orz zjt,yww
Orz黄学长。。。但是他们加强了数据。。现在有负数了诶。。您的代码没有离散化。。
– –
嘛。。。其实并不需要在意。。。
不仅仅有负数吧 还需要 LL
不需要吧。。我没有开啊。。也过了。
为啥我是没开负数但是需要开ll
什么鬼!!!!反正加个离散就可以过了。。也许是离散和没离散的关系吧。。
我没离散……ll可能是因为标记永久化的缘故?
妹控黄学长-><-
何以见得?
换个说法,萝莉控吧~~~
话说黄学长,难道混2次元OI能够学得好吗,感觉好多OI大神都混2次元啊
讲道理我并不是萝莉控。。。也许是个*(和谐)控
是因为天天上网才会去看奇怪的东西吧
感觉很多OI大神是哲学家呢0、0
讲道理萝莉赛高
萝莉即是正义OAQ
哲♂学?
额这个 貌似“23333”会被多说直接当做垃圾评论
求问黄学长您这个程序似乎RE了?
刚刚交了一遍AC了啊