Wannafly练习赛14
Posted 人活着就是为了Chelly
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Wannafly练习赛14相关的知识,希望对你有一定的参考价值。
B(倍增)
题意:
分析:
先可以用two point预处理出以每个位置为起点的连续段<=k的下一个终点
然后对于每个询问,倍增跳就行了
时间复杂度O(nlogn)
C(扫描线处理区间询问)
题意:
分析:
先容易考虑到莫队算法,合并用并查集就行,但删除就很不方便了,而且n高达1e6,所以就无法用莫队
考虑将所有询问按照右端点r扫描线,每次维护每个位置i的答案,即区间[i,r]的答案
我们来考虑加入了一个新的数字x,会对哪些地方的答案造成修改
首先可能会有修改的地方一定是上一个x出现的位置(设为pre[x])之后
然后我们发现有一些点是修改的关键点,那就是pre[x-10],pre[x-9],...pre[x-1],pre[x+1],pre[x+2],...pre[x+10]
这些关键点中间的线段上的答案都是更改了相同的值,所以我们每次可以暴力的进行20次区间加值,询问是单点询问,这个用BIT可以轻松解决,下面我们来具体考虑一下如何修改
我们按照关键点的位置从大到小进行段修改,假设我们现在站在一个关键点上,然后这个关键点后面正好有包含0的一段[-l,r],如-3,-2,-1,1,2,那么我们应该给[cur+1,last]上的bit[l+r+1]进行区间修改,当然也别忘了要把bit[l],bit[r]对应区间减去1
时间复杂度O(nklogn+mlogn)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e6; 4 int a[maxn+5]; 5 struct wjmzbmr 6 { 7 int l,r,id; 8 bool operator < (const wjmzbmr &x) const 9 { 10 return r<x.r; 11 } 12 }q[maxn+5]; 13 char ans[maxn+5][11]; 14 int pre[maxn+5]; 15 int bit[11][maxn+5]; 16 int n,m; 17 int lowbit(int x) 18 { 19 return x&(-x); 20 } 21 void add(int *c,int k,int x) 22 { 23 if(k==0) return; 24 for(int i=k;i<=n;i+=lowbit(i)) c[i]+=x; 25 } 26 void add(int *c,int l,int r,int x) 27 { 28 //printf("%d %d %d\\n",l,r,x); 29 add(c,l,x); 30 add(c,r+1,-x); 31 } 32 int query(int *c,int k) 33 { 34 int ans=0; 35 for(int i=k;i;i-=lowbit(i)) ans+=c[i]; 36 return ans; 37 } 38 int main() 39 { 40 scanf("%d%d",&n,&m); 41 for(int i=1;i<=n;++i) scanf("%d",&a[i]); 42 for(int i=1;i<=m;++i) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i; 43 int j=1; 44 sort(q+1,q+m+1); 45 for(int i=1;i<=n;++i) 46 { 47 int x=a[i]; 48 int l=0,r=0,last=i,cur=0; 49 while(last>pre[x]) 50 { 51 cur=pre[x]; 52 if(l<=10&&x+l+1<=maxn&&pre[x+l+1]>cur) cur=pre[x+l+1]; 53 if(r<=10&&x-r-1>=1&&pre[x-r-1]>cur) cur=pre[x-r-1]; 54 //printf("%d %d %d %d\\n",cur,last,l,r); 55 if(l&&l<=10) add(bit[l],cur+1,last,-1); 56 if(r&&r<=10) add(bit[r],cur+1,last,-1); 57 if(l+r+1<=10) add(bit[l+r+1],cur+1,last,1); 58 //printf("%d %d %d %d\\n",pre[x],last,l,r); 59 while(l<=10&&x+l+1<=maxn&&pre[x+l+1]>=cur) ++l; 60 while(r<=10&&x-r-1>=1&&pre[x-r-1]>=cur) ++r; 61 last=cur; 62 if(l>10&&r>10) break; 63 //printf("%d %d %d %d\\n",pre[x],last,l,r); 64 } 65 pre[a[i]]=i; 66 while(j<=m&&q[j].r==i) 67 { 68 for(int k=1;k<=10;++k) 69 ans[q[j].id][k]=\'0\'+query(bit[k],q[j].l)%10; 70 ++j; 71 } 72 } 73 for(int i=1;i<=m;++i) puts(ans[i]+1); 74 return 0; 75 }
E(bitset)
题意:
分析:
考虑预处理出d[i][j]表示从i点开始,最短距离<=j的所有点(用bool数组表示),这是1000*1000*1000的,考虑用bitset把变成1000^3/64的
然后对于每组询问只要把所有点的对应bitset或起来就行了
至于如何求d[i][j]可以O(nm)求,也可以O(n^3/64)的压位BFS求
F(树链剖分+treap)
题意:
分析:
对于一个询问,我们要考虑点u、点u的父亲、点u的重儿子、点u的轻儿子
我们把每个点的轻儿子用平衡树存起来,或者用pbds的set
然后对于每次修改,只需要改那些重链头的点的set,这总共有logn个,所以修改的复杂度是O(log^2n)的
询问就是O(log)的了
时间复杂度是O(nlog^2n+mlogn)
我们考虑把修改操作给lazy掉,就是遇见修改先不要修改,留到询问的时候再修改,这样就把复杂度岔开了
可以证明出这样的复杂度均摊是O(nlogn+mlogn)的
以上是关于Wannafly练习赛14的主要内容,如果未能解决你的问题,请参考以下文章