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

 

posted @ 2022-04-29 01:42  menhera  阅读(94)  评论(0编辑  收藏  举报