[ICPC2022Macau]Cyclic Buffer【线段树】【动态规划】
分析:首先可以设计一个简单的dp。设dp[i][j]表示前i个数已经被选,且当前buffer的左端点在j这个位置的最小代价。注意到这个dp的有用点是很少的(有用点的意思是dp[i][j]不是由dp[i][j-1]或者dp[i][j+1]转移而来的),而且所有的有用点集中在buffer覆盖在第i个数所在的位置上。所有的dp[i][j]的状态都可以由dp[i-1]中的有用点转移而来。然后再思考有用点的性质。
设j是对i-1的一个有用点,且j~j+k-1覆盖了第i个数所在的位置,那么j对i也是有用点,因为我站在原地不动就可以取到i。
新增的有用点一定是最左覆盖i和最右覆盖i的两个buffer所在的左端点。
这时候考虑数据结构维护。对于一个数i,寻找最右覆盖的大于它的mex,最左覆盖的大于它的mex。然后转移的时候直接跳过原地不动可以取到的情况,更新新增的有用点的情况。
(写的不清不楚,因为是给自己看的)
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 1020000; 5 6 int n,k; 7 int a[maxn*2],b[maxn]; 8 9 long long l[maxn],r[maxn]; 10 int nl[maxn],nr[maxn]; 11 12 int T[maxn*4]; 13 14 void update(long long ori,int rightpos,int upd,long long &ans){ 15 if(upd == -1){ans = min(ans,ori);return;} 16 long long u1 = ori+min(abs(b[upd]-rightpos),n-abs(b[upd]-rightpos)); 17 if(l[upd] == -1) l[upd] = u1; 18 else l[upd] = min(l[upd],u1); 19 int leftpos = (rightpos-k+1+n)%n;if(leftpos == 0) leftpos = n; 20 long long u2 = ori+min(abs(b[upd]-leftpos),n-abs(b[upd]-leftpos)); 21 if(r[upd] == -1) r[upd] = u2; 22 else r[upd] = min(r[upd],u2); 23 } 24 25 void add(int now,int l,int r,int pos,int dt){ 26 if(l == r){T[now]+=dt;return;} 27 else{ 28 int mid = (l+r)/2; 29 if(pos <= mid) add(now<<1,l,mid,pos,dt); 30 else add(now<<1|1,mid+1,r,pos,dt); 31 T[now]+=dt; 32 } 33 } 34 int query(int now,int tl,int tr,int l,int r){ 35 if(tl >= l && tr <= r){ 36 if(T[now] == tr-tl+1) return -1; 37 else{ 38 if(tl == tr) return tl; 39 int mid = (tl+tr)/2; 40 int how = query(now<<1,tl,mid,l,r); 41 if(how != -1) return how; 42 else return query(now<<1|1,mid+1,tr,l,r); 43 } 44 } 45 if(tl > r || tr < l) return -1; 46 int mid = (tl+tr)/2; 47 int how = query(now<<1,tl,mid,l,r); 48 if(how != -1) return how; 49 else return query(now<<1|1,mid+1,tr,l,r); 50 } 51 52 int main(){ 53 ios::sync_with_stdio(false); 54 int t; cin >> t; 55 while(t--){ 56 cin >> n >> k; 57 for(int i=1;i<=n;i++) { 58 cin >> a[i]; 59 a[n+i] = a[i]; 60 b[a[i]] = i; 61 l[i] = r[i] = -1; 62 nl[i] = nr[i] = 0; 63 } 64 for(int i=k;i>=1;i--) add(1,1,n,a[i],1); 65 for(int i=n;i>=1;i--){ 66 add(1,1,n,a[i+k],-1); 67 add(1,1,n,a[i],1); 68 nr[a[i]] = query(1,1,n,a[i],n); 69 } 70 int st = query(1,1,n,1,n); 71 for(int i=1;i<=4*n;i++) T[i] = 0; 72 if(st == -1){cout<<0<<endl;continue;} 73 l[st] = min(abs(b[st]-k),n-abs(k-b[st])); 74 r[st] = min(b[st]-1,n+1-b[st]); 75 for(int i=n-k+1;i<=n;i++) add(1,1,n,a[i],1); 76 for(int i=n+1;i<=2*n;i++){ 77 add(1,1,n,a[i-k],-1); 78 add(1,1,n,a[i],1); 79 nl[a[i-n]] = query(1,1,n,a[i],n); 80 } 81 for(int i=1;i<=4*n;i++) T[i] = 0; 82 long long ans = 1e18; 83 for(int i=st;i<=n;i++){ 84 if(l[i] != -1){ 85 update(l[i],b[i],nl[i],ans); 86 } 87 if(r[i] != -1){ 88 update(r[i],((b[i]+k-1)-1)%n+1,nr[i],ans); 89 } 90 } 91 cout<<ans<<endl; 92 } 93 }