Nowcoder 提高组练习赛-R1

  https://www.nowcoder.com/acm/contest/172#question

  单人报名300元,五人合报免费,于是就和学弟同学学长们组了一个三世同堂的队伍,高一的学长wzhqwq;同一届的同学们:zutter,asuldb;以及不是学长却胜似学长的qwaszx。

  感觉这套题比NOIP还是要难一些的。

  A:中位数:https://www.nowcoder.com/acm/contest/172/A

  题意概述:给定一个长度为$n$的序列,求它的所有长度大于等于$len$的子序列的中位数的最大值。$n,len<=10^5$

  NOIP的D1T1不应该是签到题吗...?这个题非常清奇啊。想了半小时(中间电脑死机2次)之后发现自己确实不会做,就写了一个$O(N^2logN)$的对顶堆做法,拿分倒是非常稳,确实得了60。

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # include <queue>
 4 # define R register int
 5  
 6 using namespace std;
 7  
 8 const int maxn=100005;
 9 int n,len,ans=0;
10 int a[maxn];
11 priority_queue <int,vector<int> > q1;
12 priority_queue <int,vector<int>,greater<int> > q2;
13  
14 int read()
15 {
16     int x=0,f=1;
17     char c=getchar();
18     while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); }
19     while (isdigit(c))  { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
20     return x*f;
21 }
22  
23 int ask()
24 {
25     if(q1.size()<q2.size())
26         return q2.top();
27     return q1.top();
28 }
29  
30 int main()
31 {
32     n=read(),len=read();
33     for (R i=1;i<=n;++i)
34         a[i]=read();
35     for (R i=1;i<=n;++i)
36     {
37         if(i+len-1>n) break;
38         while (q1.size()) q1.pop();
39         while (q2.size()) q2.pop();
40         q1.push(-1000000009);
41         q2.push(1000000009);
42         for (R j=i;j<=n;++j)
43         {
44             if(a[j]<q2.top()) q1.push(a[j]);
45             else q2.push(a[j]);
46             while (q1.size()<q2.size())
47             {
48                 q1.push(q2.top());
49                 q2.pop();
50             }
51             while (q2.size()<q1.size())
52             {
53                 q2.push(q1.top());
54                 q1.pop();
55             }
56             if(j-i+1>=len) ans=max(ans,ask());
57         }
58     }
59     printf("%d",ans);
60     return 0;
61 }
中位数(60pts)

  现在来说一下正解:虽然答案本身并不单调,然而有一个东西是显而易见非常单调的:设$f_x=[ans>=x]$,这是一个布尔函数,它是单调的。所以二分这个答案,接下来考虑怎么$check$。离散化,大于等于$x$的设置成$1$,小于$x$的设成$-1$,如果一段长度大于$len$的区间的和大于$0$,就说明$f_x=true$。注意这里一定不能写成大于等于,因为这个$x$不一定是这个序列中真实存在的数,如果比它大,比它小的数一样多,这时的中位数是有可能小于$x$的(偶数长度的序列取小一点的那个值)。接下来的问题是怎么判断是否有区间的和大于$0$,其实就是求一段长度大于等于len的区间的和的最大值,用类似于单调队列优化dp的思想即可,不过只需要维护一个最小值。

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # include <queue>
 4 # define R register int
 5 
 6 using namespace std;
 7 
 8 const int maxn=100005;
 9 int n,len,ans=0;
10 int a[maxn],b[maxn];
11 int s[maxn],minn[maxn],maxx=0;
12 
13 int read()
14 {
15     int x=0,f=1;
16     char c=getchar();
17     while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); }
18     while (isdigit(c))  { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
19     return x*f;    
20 }
21 
22 bool check (int x)
23 {
24     for (R i=1;i<=n;++i)
25         if(a[i]<x) b[i]=-1;
26         else b[i]=1;
27     for (R i=1;i<=n;++i)
28         s[i]=s[i-1]+b[i];
29     minn[1]=s[1];
30     for (R i=2;i<=n;++i)
31         minn[i]=min(minn[i-1],s[i]);
32     for (R i=len;i<=n;++i)
33         if(s[i]>minn[i-len]) 
34             return true;
35     return false;
36 }
37 
38 int main()
39 {
40     n=read(),len=read();
41     for (R i=1;i<=n;++i)
42         a[i]=read(),maxx=max(maxx,a[i]);
43     int mid,ans=1,l=1,r=maxx;
44     while (l<=r)
45     {
46         mid=(l-r)/2+r;
47         if(check(mid))
48             ans=max(ans,mid),l=mid+1;
49         else 
50             r=mid-1;
51     }
52     printf("%d",ans);
53     return 0;
54 }
中位数

  B.数数字:https://www.nowcoder.com/acm/contest/172/B

  题意概述:对于一个数$x$,定义$f_x$为$x$的各个数位的乘积。对于$L<=x<=R$,问有多少$x$满足,$L_1<=f_x<=R_1$。

  $0<=L,R,L_1,R_1 <= 10^18,L<=R,L_1<=R_1$

  暴力可以得好多分,如果注意特判l=0的情况就会有50分的好成绩,但是考试时忘了这种问题所以只有45。正解是分解质因数的数位dp。

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # define R register int
 4 
 5 using namespace std;
 6 
 7 int l,r,ll,rr,ans;
 8 int x,s;
 9 
10 int read()
11 {
12     int x=0,f=1;
13     char c=getchar();
14     while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); }
15     while (isdigit(c))  { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
16     return x*f;    
17 }
18 
19 int main()
20 {
21     scanf("%d%d%d%d",&l,&r,&ll,&rr);
22     for (int i=l;i<=r;++i)
23     {
24         s=1;
25         x=i;
26         if(x==0) s=0;
27         while (x)
28         {
29             s*=x%10;
30             x/=10;
31         }
32         if(ll<=s&&s<=rr) ans++;
33     }
34     printf("%d",ans);
35     return 0;
36 }
数数字(50pts)

  C.保护:https://www.nowcoder.com/acm/contest/172/C

  题意概述:给定一颗$n$个点的树,以及$m$条路径,多组询问:给出一组$u$,$k$,询问从u到根的路径上满足(从$u$到$p$的路径整体都被至少k条路径覆盖过)的深度最浅的点$p.n,m,q<=200000$

  解释一下,$u->p$路径被路径$x$覆盖当且仅当$u->p$这段路径上的每一条边都属于路径$x$.

  看到这道题就觉得非常像天天爱跑步。想到了一个最差能到$O(q*max(n,m))$的算法,只得了$40$。这个做法是这样的,对于每一条路径首先求出它的$lca$,用$n$个$vector$,在每条路径的两个端点分别$push$一个编号进去,在$lca$处$push$一个编号的相反数,表示从这里再往上就不再有这条路径了。如果一条$u->p$路径被路径$x$所覆盖,则$x$必然有一个端点在$u$的子树内,而且它的$lca$不能在$p$的子树内。这样首先从$u$点向下递归,用一个$bitset$作为桶,统计有哪些路径覆盖了点u,再向上爬,每走一步就把$lca$在这里的路径删除去掉,直到路径的数目小于$k$为止。 

  
  1 # include <cstdio>
  2 # include <iostream>
  3 # include <vector>
  4 # include <bitset>
  5 # define R register int
  6 
  7 using namespace std;
  8 
  9 const int maxn=200005;
 10 int q,u,k,n,m,x,y,lca[maxn],s[maxn],t[maxn],cnt,dep[maxn],F[maxn][20];
 11 int firs[maxn],h,ans;
 12 vector<int> v[maxn];
 13 bitset <maxn> T;
 14 struct edge
 15 {
 16     int too,nex;
 17 }g[maxn<<1];
 18 
 19 int read()
 20 {
 21     int x=0,f=1;
 22     char c=getchar();
 23     while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); }
 24     while (isdigit(c))  { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
 25     return x*f;    
 26 }
 27 
 28 void add (int x,int y)
 29 {
 30     g[++h].too=y;
 31     g[h].nex=firs[x];
 32     firs[x]=h;
 33 }
 34 
 35 void dfs (int x)
 36 {
 37     int j;
 38     for (R i=firs[x];i;i=g[i].nex)
 39     {
 40         j=g[i].too;
 41         if(dep[j]) continue;
 42         dep[j]=dep[x]+1;
 43         F[j][0]=x;
 44         for (R i=1;i<=19;++i)
 45             F[j][i]=F[ F[j][i-1] ][i-1];
 46         dfs(j);
 47     }
 48 }
 49 
 50 void dfs1 (int x)
 51 {
 52     int j,siz=v[x].size();
 53     for (R i=0;i<siz;++i)
 54         if(v[x][i]>0&&T[ v[x][i] ]==0) 
 55             cnt++,T[ v[x][i] ]=1;
 56     for (R i=firs[x];i;i=g[i].nex)
 57     {
 58         j=g[i].too;
 59         if(dep[j]<dep[x]) continue;
 60         dfs1(j);
 61     }
 62     if(x==u) return;
 63     for (R i=0;i<siz;++i)
 64         if(v[x][i]<0&&T[ -v[x][i] ]==1)
 65             cnt--,T[ -v[x][i] ]=0;    
 66 }
 67 
 68 inline int LCA (int x,int y)
 69 {
 70     if(dep[x]>dep[y]) swap(x,y);
 71     for (R i=19;i>=0;--i)
 72         if(dep[y]-(1<<i)>=dep[x]) y=F[y][i];
 73     if(x==y) return x;    
 74     for (R i=19;i>=0;--i)
 75         if(F[x][i]!=F[y][i]) x=F[x][i],y=F[y][i];
 76     return F[x][0];
 77 }
 78 
 79 void up (int x)
 80 {
 81     if(cnt<k) return ;
 82     ans=dep[u]-dep[x];
 83     int siz=v[x].size();
 84     for (R i=0;i<siz;++i)
 85         if(v[x][i]<0&&T[ -v[x][i] ]==1)
 86             cnt--,T[ -v[x][i] ]=0;
 87     up(F[x][0]);
 88 }
 89 
 90 int main()
 91 {
 92     scanf("%d%d",&n,&m);
 93     for (R i=1;i<n;++i)
 94     {
 95         x=read();
 96         y=read();
 97         add(x,y);
 98         add(y,x);
 99     }
100     dep[1]=1;
101     dfs(1);
102     for (R i=1;i<=m;++i)
103     {
104         s[i]=read();
105         t[i]=read();
106         lca[i]=LCA(s[i],t[i]);
107         v[ s[i] ].push_back(i);
108         v[ t[i] ].push_back(i);
109         v[ lca[i] ].push_back(-i);
110     }
111     scanf("%d",&q);
112     for (R i=1;i<=q;++i)
113     {
114         T.reset();
115         cnt=0,ans=0;
116         u=read();
117         k=read();
118         dfs1(u);
119         up(u);
120         printf("%d
",ans);
121     }
122     return 0;
123 }
保护(40pts)

  

  最后放一张惨淡的成绩,下次要加油啦 (在nowcoder,享受一场上蓝的快乐)

---shzr

原文地址:https://www.cnblogs.com/shzr/p/9614207.html