其他分享
首页 > 其他分享> > [loj3006]破壁

[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