[loj3006]破壁
作者:互联网
显然可以直接贪心,即每一次移动尽量少的距离,时间复杂度为$o(nm)$
不难发现,以下两类攻击可以被删除:
1.连续两次攻击位置相同,则删除其中一次攻击
2.连续三次攻击位置形如$a<b<c$或$a>b>c$,则删除中间的攻击
此时,攻击位置即形如$a_{1}<a_{2}>a_{3}<a_{4}>...$(或交换$<$和$>$)
取其中最近的两次攻击,假设为$a_{x}$和$a_{x+1}$,对防壁长度$len$分类讨论:
1.若$len<|a_{x}-a_{x+1}|$,此时答案即$\sum_{i=1}^{m-1}|a_{i}-a_{i+1}|$(需要特殊处理前两次攻击)
2.若$len\ge |a_{x}-a_{x+1}|$,此时以下两类攻击可以被删除——
(1)若$x\ge 2$,则删除$a_{x}$,具体原因如下:
不妨假设$a_{x}<a_{x+1}$,根据最近性有$a_{x-1}>a_{x+1}$,进而在操作$a_{x}$后一定已覆盖$a_{x+1}$
(2)若$x+1<m$,则删除$a_{x+1}$,具体原因类似
当$m\ge 3$时显然每一轮至少删除一个,并需要在删除处重新处理开头的两种删除
具体可以用链表+set(找最小)实现,另外当$m\le 2$直接暴力即可
时间复杂度为$o(n+m\log m)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 200005 4 #define ll long long 5 int n,m,n0,m0,x,L[N],R[N],id[N],a[N],pre[N],nex[N]; 6 ll sum,ans[N]; 7 set<pair<int,int> >S; 8 int get_len(int k){ 9 return R[k]-L[k]; 10 } 11 int get_val(int k){ 12 return abs(a[k]-a[nex[k]]); 13 } 14 bool cmp(int x,int y){ 15 return get_len(x)<get_len(y); 16 } 17 void add_S(int k){ 18 sum+=get_val(k),S.insert(make_pair(get_val(k),k)); 19 } 20 void del_S(int k){ 21 sum-=get_val(k),S.erase(make_pair(get_val(k),k)); 22 } 23 void del(int k){ 24 m0--; 25 if (pre[k])del_S(pre[k]); 26 if (nex[k])del_S(k); 27 nex[pre[k]]=nex[k],pre[nex[k]]=pre[k]; 28 if ((pre[k])&&(nex[k]))add_S(pre[k]); 29 } 30 void move(int k,int x){ 31 if (x<L[k])R[k]-=L[k]-x,ans[k]+=L[k]-x,L[k]=x; 32 if (R[k]<x)L[k]+=x-R[k],ans[k]+=x-R[k],R[k]=x; 33 } 34 int main(){ 35 scanf("%d%d",&n,&m); 36 for(int i=1;i<=n;i++){ 37 scanf("%d%d",&L[i],&R[i]); 38 id[i]=i; 39 } 40 sort(id+1,id+n+1,cmp); 41 for(int i=1;i<=m;i++){ 42 scanf("%d",&x); 43 if ((m0)&&(a[m0]==x))continue; 44 while ((m0>1)&&((a[m0-1]<a[m0])==(a[m0]<x)))m0--; 45 a[++m0]=x; 46 } 47 n0=1,pre[0]=m0; 48 for(int i=0;i<m0;i++)nex[i]=i+1,pre[i+1]=i; 49 for(int i=1;i<m0;i++)add_S(i); 50 while (m0>2){ 51 int k=(*S.begin()).second; 52 while ((n0<=n)&&(get_len(id[n0])<get_val(k))){ 53 ans[id[n0]]=sum-get_val(nex[0])-(ll)(m0-2)*get_len(id[n0]); 54 move(id[n0],a[nex[0]]),move(id[n0],a[nex[nex[0]]]); 55 n0++; 56 } 57 if (pre[k])del(nex[k]); 58 if (nex[k])del(k),k=pre[k]; 59 while (1){ 60 int nk=nex[k]; 61 if ((!k)||(!nk))break; 62 if (a[k]==a[nk]){del(nk);continue;} 63 if ((pre[k])&&((a[pre[k]]<a[k])==(a[k]<a[nk]))){del(k);continue;} 64 if ((nex[nk])&&((a[k]<a[nk])==(a[nk]<a[nex[nk]]))){del(nk);continue;} 65 break; 66 } 67 } 68 for(int i=n0;i<=n;i++){ 69 move(id[i],a[nex[0]]); 70 if (m0>1)move(id[i],a[nex[nex[0]]]); 71 } 72 for(int i=1;i<=n;i++)printf("%lld\n",ans[i]); 73 return 0; 74 }View Code
标签:删除,int,len,攻击,ge,nex,loj3006,破壁 来源: https://www.cnblogs.com/PYWBKTDA/p/16058793.html