考场上写的$O(nlogn)$做法,具体思想是把深度从低到高排个序,开一个标记数组,每次加入的时候标记当前位置并判断:如果当前加入的位置两边都被标记过,则下次的贡献-1,若两边都没有被标记过,则贡献+1。预先把数组的0和n+1标记一下。
考场代码:
1 #include<cstdio> 2 #include<algorithm> 3 #define R register 4 #define I inline 5 using namespace std; 6 const int S=100010; 7 I int rd(){ 8 R int f=0; R char c=getchar(); 9 while(c<48||c>57) c=getchar(); 10 while(c>47&&c<58) f=f*10+(c^48),c=getchar(); 11 return f; 12 } 13 int b[S]; 14 struct node{ 15 int d,s; 16 int operator <(const node &a)const{return d==a.d?s<a.s:d<a.d;} 17 }f[S]; 18 I int max(int x,int y){return x>y?x:y;} 19 int main(){ 20 R int n=rd(),i,j,k=1,p,u,o=0; 21 for(i=1;i<=n;++i) f[i].d=rd(),p=max(p,f[i].d),f[i].s=i; 22 sort(f+1,f+1+n),b[0]=1,b[n+1]=1; 23 for(i=1,j=0;i<=n;j=f[i].d,++i){ 24 o+=(f[i].d-j)*k; 25 while(f[i].d==f[i+1].d){ 26 u=f[i].s,b[u]=1; 27 if(b[u+1]&&b[u-1]) --k; 28 if(!b[u+1]&&!b[u-1]) ++k; 29 ++i; 30 } 31 u=f[i].s,b[u]=i; 32 if(b[u+1]&&b[u-1]) --k; 33 if(!b[u+1]&&!b[u-1]) ++k; 34 } 35 printf("%d",o); 36 return 0; 37 }
事实上这题可以$O(n)$贪心:从左往右扫,如果后一个比前一个大就计算贡献,事实证明这种贪心是正确的。
1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include<algorithm> 5 #define R register 6 #define I inline 7 using namespace std; 8 const int S=100010; 9 10 char buf[S],*p1,*p2; 11 I char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;} 12 I int rd(){ 13 R int f=0; R char c=gc(); 14 while(c<48||c>57) c=gc(); 15 while(c>47&&c<58) f=(f<<3)+(f<<1)+(c^48),c=gc(); 16 return f; 17 } 18 19 int main(){ 20 R int n=rd(),o=0,i,a=0,b=rd(); 21 for(i=2;i<=n;++i){ 22 if(b>a) o+=b-a; 23 a=b,b=rd(); 24 } 25 if(b>a) o+=b-a; 26 printf("%d",o); 27 return 0; 28 }