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 }
View Code

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)的

 

posted @ 2018-04-03 18:23  Chellyutaha  阅读(136)  评论(0编辑  收藏  举报