题意:
给一纸条,两种操作:
1.将左侧长度为$x$的纸条向右翻折。
2.询问位于$[l,r]$的纸条总长度。
解法:
考虑启发式,每一次一个小纸条折叠我们可以看做是一次合并,如果我们每一次将较小的纸条并入较大的纸条。
这样对于每一个数字,包含它的纸条长度每次至少乘以2,这样每一个数字变动$logn$次。
对于一个$2x > len$过大的操作,我们可以转化为将右面的$len-x$个翻折过来,并执行一次翻转操作。
用$rev$记录是否翻转,分类讨论即可。
同时用线段树记录区间和。
这样总效率$O(nlog^2n)$
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 #define N 100010 6 #define lb(x) ((x)&(-x)) 7 8 using namespace std; 9 10 int n,q,L,R; 11 int sumv[N],a[N]; 12 13 void add(int x,int v) 14 { 15 for(int i=x;i<=n&&i;i+=lb(i)) 16 sumv[i]+=v; 17 } 18 19 int ask(int x) 20 { 21 int ans=0; 22 for(int i=x;i>0;i-=lb(i)) ans+=sumv[i]; 23 return ans; 24 } 25 26 void solve(int cnt,int typ) 27 { 28 if(typ==0) 29 { 30 for(int i=0;i<cnt;i++) 31 { 32 a[L+cnt+i] += a[L+cnt-i-1]; 33 add(L+cnt+i,a[L+cnt-i-1]); 34 add(L+cnt-i-1,-a[L+cnt-i-1]); 35 a[L+cnt-i-1]=0; 36 } 37 L+=cnt; 38 } 39 else 40 { 41 for(int i=0;i<cnt;i++) 42 { 43 a[R-cnt-i] += a[R-cnt+i+1]; 44 add(R-cnt-i,a[R-cnt+i+1]); 45 add(R-cnt+i+1,-a[R-cnt+i+1]); 46 a[R-cnt+i+1]=0; 47 } 48 R-=cnt; 49 } 50 } 51 52 int main() 53 { 54 while(~scanf("%d%d",&n,&q)) 55 { 56 for(int i=1;i<=n;i++) sumv[i]=0,a[i]=1; 57 for(int i=1;i<=n;i++) add(i,1); 58 L=1; 59 R=n; 60 int rev=0; 61 for(int i=1,cmd,x,y;i<=q;i++) 62 { 63 scanf("%d%d",&cmd,&x); 64 if(cmd==1) 65 { 66 int len = R-L+1; 67 if(x*2<=len) solve(x,rev); 68 else solve(len-x,rev^1),rev^=1; 69 } 70 else 71 { 72 int ans; 73 scanf("%d",&y); 74 if(!rev) ans = ask(L+y-1)-ask(L+x-1); 75 else ans = ask(R-x)-ask(R-y); 76 printf("%d ",ans); 77 } 78 } 79 } 80 }