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 }
现在来说一下正解:虽然答案本身并不单调,然而有一个东西是显而易见非常单调的:设$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 }
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\n",ans); 121 } 122 return 0; 123 }
最后放一张惨淡的成绩,下次要加油啦 (在nowcoder,享受一场上蓝的快乐)
---shzr