1. 程式人生 > >BZOJ 4373算術天才⑨與等差數列(線段樹)

BZOJ 4373算術天才⑨與等差數列(線段樹)

ring 一個 open num tree chang main tor 函數

題意:
給你一個長度為n的序列,有m個操作,寫一個程序支持以下兩個操作:

1. 修改一個值

2. 給出三個數l,r,k,

詢問:如果把區間[l,r]的數從小到大排序,能否形成公差為k的等差數列。
n,m≤300000 0≤k,a[i]≤109

題解

這題坑我很久。

一眼望去這題不可作。(倒是想到維護最小值和最大值。)

然後翻了題解。發現我的想法和題解差不多。

直接維護區間等差數列顯然很難,那麽考慮一下:如果區間[l,r] (l < r)排序後能形成公差為k(k>0)的等差數列,要滿足什麽條件?

1. 很顯然,假設min是區間最小值,max是區間最大值,那麽 min+k(r?l)=max

2. 區間相鄰兩個數之差的絕對值的gcd=k

3. 區間沒有重復的數

前兩個條件 線段樹直接維護就好

第三個條件:

對於每個權值開個set,值為位置(離散化標號)

然後維護一個pre[i],表示當前a[i]這個值,在i前面最後一次出現的位置。那麽滿足第3個條件,當且僅當區間[l,r]的pre的最大值小於l。這個也是用線段樹維護。

然後看修改操作:在set上找前一個數、後一個數,然後修改相應的值


然後發現不會用set求前驅後繼。然後花了幾個小時學。

(一開始翻的博客都只介紹set的函數。然後翻到一篇講求前去後繼的,一眼掃完就會了,看好博客是多麽重要啊)

技術分享圖片

然後這個iter是個叠代器。*iter是第一個比x大的數的實際下標,也就是後繼的下標(如果iter是q.end()說明沒有後繼)

技術分享圖片

然後這個it也是個叠代器。*it是第一個大於等於x的數的實際下標。然後it--不是減實際的下標,而是使set中的下標。

假如*it是第一個比x小的數的下標,it--後*it就是第二個比x小的數的實際下標。

所以把x插入set後用上面的式子求出it,it--後*it就是x的前驅的實際下標

技術分享圖片
  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<set
> 7 #include<map> 8 using namespace std; 9 const int N=300010; 10 set<int>q[N<<1]; 11 map<int,int> ma; 12 int a[N],pre[N],n,m,num,cnt; 13 int gcd(int x,int y){ 14 if(y==0)return x; 15 if(x==0)return y; 16 else return gcd(y,x%y); 17 } 18 struct tree{ 19 int l,r,mx,mn,gc,mnp,ln,rn; 20 }tr[N<<2]; 21 void update(int now){ 22 tr[now].ln=tr[now*2].ln; 23 tr[now].rn=tr[now*2+1].rn; 24 tr[now].gc=abs(tr[now*2].rn-tr[now*2+1].ln); 25 tr[now].gc=gcd(tr[now].gc,gcd(tr[now*2].gc,tr[now*2+1].gc)); 26 tr[now].mn=min(tr[now*2].mn,tr[now*2+1].mn); 27 tr[now].mx=max(tr[now*2].mx,tr[now*2+1].mx); 28 tr[now].mnp=max(tr[now*2].mnp,tr[now*2+1].mnp); 29 } 30 void build(int l,int r,int now){ 31 tr[now].l=l;tr[now].r=r; 32 if(l==r){ 33 tr[now].mx=tr[now].mn=tr[now].ln=tr[now].rn=a[l]; 34 tr[now].mnp=pre[l]; 35 return; 36 } 37 int mid=(tr[now].l+tr[now].r)>>1; 38 build(l,mid,now*2); 39 build(mid+1,r,now*2+1); 40 update(now); 41 } 42 void change(int x,int now){ 43 if(tr[now].l==tr[now].r){ 44 tr[now].mx=tr[now].mn=tr[now].ln=tr[now].rn=a[tr[now].l]; 45 tr[now].mnp=pre[tr[now].l]; 46 return; 47 } 48 int mid=(tr[now].l+tr[now].r)>>1; 49 if(x>mid)change(x,now*2+1); 50 else change(x,now*2); 51 update(now); 52 } 53 int getmin(int l,int r,int now){ 54 if(tr[now].l==l&&tr[now].r==r){ 55 return tr[now].mn; 56 } 57 int mid=(tr[now].l+tr[now].r)>>1; 58 if(l>mid)return getmin(l,r,now*2+1); 59 else if(r<=mid)return getmin(l,r,now*2); 60 else { 61 return min(getmin(l,mid,now*2),getmin(mid+1,r,now*2+1)); 62 } 63 } 64 int getmax(int l,int r,int now){ 65 if(tr[now].l==l&&tr[now].r==r){ 66 return tr[now].mx; 67 } 68 int mid=(tr[now].l+tr[now].r)>>1; 69 if(l>mid)return getmax(l,r,now*2+1); 70 else if(r<=mid)return getmax(l,r,now*2); 71 else { 72 return max(getmax(l,mid,now*2),getmax(mid+1,r,now*2+1)); 73 } 74 } 75 int getgcd(int l,int r,int now){ 76 if(tr[now].l==l&&tr[now].r==r){ 77 return tr[now].gc; 78 } 79 int mid=(tr[now].l+tr[now].r)>>1; 80 if(l>mid)return getgcd(l,r,now*2+1); 81 else if(r<=mid)return getgcd(l,r,now*2); 82 else { 83 return gcd(gcd(getgcd(l,mid,now*2),getgcd(mid+1,r,now*2+1)),abs(tr[now*2].rn-tr[now*2+1].ln)); 84 } 85 } 86 int getpre(int l,int r,int now){ 87 if(tr[now].l==l&&tr[now].r==r){ 88 return tr[now].mnp; 89 } 90 int mid=(tr[now].l+tr[now].r)>>1; 91 if(l>mid)return getpre(l,r,now*2+1); 92 else if(r<=mid)return getpre(l,r,now*2); 93 else { 94 return max(getpre(l,mid,now*2),getpre(mid+1,r,now*2+1)); 95 } 96 } 97 int main(){ 98 scanf("%d%d",&n,&m); 99 for(int i=1;i<=n;i++){ 100 scanf("%d",&a[i]); 101 if(ma[a[i]]==0){ 102 ma[a[i]]=++num; 103 q[ma[a[i]]].insert(i); 104 } 105 else{ 106 q[ma[a[i]]].insert(i); 107 set<int>::iterator it=q[ma[a[i]]].lower_bound(i); 108 it--; 109 pre[i]=*it; 110 } 111 } 112 build(1,n,1); 113 for(int i=1;i<=m;i++){ 114 int k; 115 scanf("%d",&k); 116 if(k==1){ 117 int x,y; 118 scanf("%d%d",&x,&y); 119 x^=cnt;y^=cnt; 120 set<int>::iterator iter=q[ma[a[x]]].upper_bound(x); 121 if(iter!=q[ma[a[x]]].end()){ 122 pre[*iter]=pre[x]; 123 change(*iter,1); 124 } 125 q[ma[a[x]]].erase(x); 126 a[x]=y; 127 if(ma[y]==0){ 128 ma[y]=++num; 129 q[ma[y]].insert(x); 130 pre[x]=0; 131 } 132 else{ 133 q[ma[y]].insert(x); 134 set<int>::iterator it=q[ma[y]].lower_bound(x); 135 if(it!=q[ma[y]].begin()){ 136 it--; 137 pre[i]=*it; 138 } 139 iter=q[ma[y]].upper_bound(x); 140 if(iter!=q[ma[y]].end()){ 141 pre[*iter]=x; 142 change(*iter,1); 143 } 144 } 145 change(x,1); 146 } 147 else{ 148 int l;int r;int x; 149 scanf("%d%d%d",&l,&r,&x); 150 l^=cnt;r^=cnt;x^=cnt; 151 int mn=getmin(l,r,1); 152 int mx=getmax(l,r,1); 153 if(l==r){ 154 printf("Yes\n"); 155 cnt++; 156 continue; 157 } 158 if(x==0){ 159 if(mn==mx){ 160 printf("Yes\n"); 161 cnt++; 162 } 163 else printf("No\n"); 164 continue; 165 } 166 if(mn+(r-l)*x!=mx){ 167 printf("No\n"); 168 continue; 169 } 170 int GCD=getgcd(l,r,1); 171 if(GCD%x!=0){ 172 printf("No\n"); 173 continue; 174 } 175 int PRE=getpre(l,r,1); 176 if(PRE>=l){ 177 printf("No\n"); 178 continue; 179 } 180 printf("Yes\n"); 181 cnt++; 182 } 183 } 184 return 0; 185 }
View Code

BZOJ 4373算術天才⑨與等差數列(線段樹)