JZOJ 3292. 发牌
题目
分析
- 刚看到这个题
- 还以为是一个数学题
- 但仔细看看发现有点怪
- 分析一下
- 不难发现
- 我们设当前拍顶为k,当前要洗n次牌,牌堆里还剩h张牌
- 我们要找的就是(k+n)%h的数
- 但是我们在之前可能有删除数
- 所以得出来的并不是原数
- 不难发现得出的是当前第k大的数
- ok,权值线段树,求区间第k大
- 那就再给你们讲讲,原理吧
- 我们在构建线段树时,把区间之间的宽度求出来也就是l-r+1
- 当我们求第k大的时候
- 如果能向左找直接找
- 但是向右就一定要减去本来向左的宽度
- 因为我们在构建线段树的时候,右边是mid+1
- 然后找出之后我们之间在含k的区间之间减一就好了
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 using namespace std; 6 struct sb 7 { 8 int l,r,val; 9 }t[700001*4]; 10 inline int read(){ 11 int date=0,w=1;char c=0; 12 while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} 13 while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} 14 return date*w; 15 } 16 void build(int a,int b,int k) 17 { 18 t[k].l=a; t[k].r=b; t[k].val=t[k].r-t[k].l+1; 19 if (a==b) return; 20 int mid=(a+b)>>1; 21 build(a,mid,k<<1); 22 build(mid+1,b,k<<1|1); 23 } 24 int find(int x,int k) 25 { 26 if (t[k].l==t[k].r) return t[k].l; 27 if (x<=t[k<<1].val) return find(x,k<<1); 28 else return find(x-t[k<<1].val,k<<1|1); 29 } 30 void del(int x,int k) 31 { 32 t[k].val--; 33 if (t[k].l==t[k].r) return; 34 int mid=(t[k].l+t[k].r)>>1; 35 if (x<=mid) del(x,k<<1); 36 else del(x,k<<1|1); 37 } 38 int main () 39 { 40 int n,k=1; 41 cin>>n; 42 build(1,n,1); 43 for (int i=1,x;i<=n;i++) 44 { 45 x=read(); 46 k=(k+x)%(n-i+1); 47 if (k==0) 48 k=n-i+1; 49 int ans=find(k,1); 50 printf("%d\n",ans); 51 del(ans,1); 52 } 53 }
为何要逼自己长大,去闯不该闯的荒唐