[NOIp2018]铺设道路

[LG传送门]

考场上写的$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 }

猜你喜欢

转载自www.cnblogs.com/cj-chd/p/9950788.html