• 2019-2020Nowcoder Girl初赛题解


      写了一天计算几何,心态崩了,水一篇题解休息休息。

      emmmm,如果您是一名现役OIer/CSPer,那看这篇文章也许并不能在你的生命中留下些什么(潮子语录),因为相比NOIP/CSP这个比赛其实比较简单。

      在这里我不概括题意,因为现在有重现赛,所有人都可以看题。

    A 牛妹爱整除

      首先,你知道为什么10进制下%3具有如此美妙的性质吗?

      用B进制来表示一个数:$sum_{i=0}^{infin}a_iB^i$,我们希望:$(sum_{i=0}^{infin}a_iB^i)\%p=(sum_{i=0}^{infin}a_i)\%p$,因为 $a_i$ 是任意的,我们不能依赖它的性质,那唯一的方法就是让 $B$ 的任意次方 $\%p$ 都等于1了。可以发现,当且仅当 $B\%p=1$ ,这个式子才是成立的。对于这道题,我们直接输出k+1就可以了;事实上,只要不超过题目的最大限制,任意的 $xk+1$ 都可以。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # define R register int
     4 
     5 using namespace std;
     6 
     7 int k;
     8 
     9 int main()
    10 {
    11     scanf("%d",&k);
    12     printf("%d",k+1);
    13     return 0;
    14 }
    A

    B 吃桃

      从某种意义上来讲,这个是不是比A还简单?

      设 $f[i]$ 表示 $i$ 节点向子树内能延伸的最长路径,一遍dfs找到最长的根路径...然后再dfs一遍输出,就没了...

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # define R register int
     4 
     5 using namespace std;
     6 
     7 const int N=100005;
     8 int n,k,h,x,y,firs[N],dp[N],dep[N];
     9 struct edge { int too,nex; }g[N<<1];
    10 
    11 void add (int x,int y)
    12 {
    13     g[++h].nex=firs[x];
    14     firs[x]=h;
    15     g[h].too=y;
    16 }
    17 
    18 void dfs (int x)
    19 {
    20     int j;
    21     for (R i=firs[x];i;i=g[i].nex)
    22     {
    23         j=g[i].too;
    24         if(dep[j]) continue;
    25         dep[j]=dep[x]+1;
    26         dfs(j);
    27         dp[x]=max(dp[x],dp[j]);
    28     }
    29     dp[x]++;
    30 }
    31 
    32 void redfs (int x)
    33 {
    34     printf("%d
    ",x);
    35     if(dp[x]==1) return;
    36     int j,ans=n+10;
    37     for (R i=firs[x];i;i=g[i].nex)
    38     {
    39         j=g[i].too;
    40         if(dep[j]<dep[x]) continue;
    41         if(dp[j]+1==dp[x]) ans=min(ans,j);
    42     }
    43     redfs(ans);
    44 }
    45 
    46 int main()
    47 {
    48     scanf("%d%d",&n,&k);
    49     for (R i=1;i<n;++i)
    50     {
    51         scanf("%d%d",&x,&y);
    52         add(x,y); add(y,x);
    53     }
    54     dep[k]=1; dfs(k);
    55     redfs(k);
    56     return 0;
    57 }
    B

    C 背包

      众所周知,一道题如果在题目名称里明示某种算法,那它往往不是这种算法。不过...这道题就是背包;

      两种方法:dp[i]表示大小为i,价值最小是多少;dp[i]表示价值为i,体积最大是多少;

      显然第二种比较快,但是众所周知牛客的机器非常神,所以第一种做法也能过(事实上我看了一眼直接背包的复杂度是2e8,感觉牛客2e8稳过,就没有再进行任何思考了);最后补充一下,如果你也想尝试第一种方法,也许需要在转移时看一下放入这个物品后背包体积是否超过 $V$,这样数组只要开到 $V$;

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # define R register int
     4 
     5 using namespace std;
     6 
     7 const int N=205;
     8 const int inf=1000000000;
     9 int n,V,v[N],w[N],dp[1000006];
    10 int ans=inf;
    11 
    12 int main()
    13 {
    14     scanf("%d%d",&n,&V);
    15     for (R i=1;i<=n;++i)
    16         scanf("%d%d",&v[i],&w[i]);
    17     for (R i=1;i<=V;++i) dp[i]=inf;
    18     for (R i=1;i<=n;++i)
    19     {
    20         for (R j=V-v[i];j<V;++j)
    21             ans=min(ans,dp[j]+w[i]);
    22         if(v[i]>=V) ans=min(ans,w[i]);
    23         for (R j=V;j>=v[i];--j)
    24             dp[j]=min(dp[j],dp[ j-v[i] ]+w[i]);
    25     }
    26     ans=min(ans,dp[V]);
    27     printf("%d",ans);
    28     return 0;
    29 }
    C

    D 泡面

      赛后zutter_告诉我我的做法可能假了,因为以前CF考过类似的题,当时被Hack了好多人;然后,最后的结论是这道题和CF的那个确实不是一个做法,所以我并没有假;

      首先我们把所有人按照 $t_i$ 从小到大排序;然后模拟接水的过程就好了...具体来讲,首先,维护一个值表示“当前接水的人什么时候能接完”,然后把所有 $t_i$ 小于这个数且还没有接水的人的编号扔进一个小根堆里;每次取出堆顶作为新的接水人;如果堆空了,那就说明有一段时间并没有人接水,我们直接找到下一个还没有接过水的人 $x$,将“当前接水的人什么时候能接完”修改为 $t_x+p$ 即可。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <algorithm>
     4 # include <queue>
     5 # define R register int
     6 # define ll long long
     7 
     8 using namespace std;
     9 
    10 const int N=100005;
    11 int n,p,h,s;
    12 ll ans[N],T;
    13 struct peo
    14 {
    15     int pos,t;
    16 }a[N];
    17 priority_queue <int,vector<int>,greater<int> > q;
    18 
    19 bool cmp (peo a,peo b)
    20 {
    21     if(a.t==b.t) return a.pos<b.pos;
    22     return a.t<b.t;
    23 }
    24 
    25 int read()
    26 {
    27     int x=0;
    28     char c=getchar();
    29     while (!isdigit(c)) c=getchar();
    30     while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    31     return x;
    32 }
    33 
    34 int main()
    35 {
    36     scanf("%d%d",&n,&p);
    37     for (R i=1;i<=n;++i)
    38         a[i].pos=i,a[i].t=read();
    39     sort(a+1,a+1+n,cmp);
    40     a[n+1].t=1000000005;
    41     h=1; T=a[1].t+p; ans[ a[1].pos ]=T; 
    42     int cg=1;
    43     while(cg!=n)
    44     {
    45         while(h+1<=n&&a[h+1].t<=T) q.push(a[h+1].pos),h++;
    46         int s=q.size();
    47         if(s==0)
    48         {
    49             h++;
    50             ans[ a[h].pos ]=a[h].t+p;
    51             cg++;
    52             T=a[h].t+p;
    53             continue;
    54         }
    55         int x=q.top(); q.pop();
    56         T+=p; ans[x]=T; cg++;
    57     }
    58     for (R i=1;i<=n;++i) printf("%lld ",ans[i]);
    59     return 0;
    60 }
    D

    E 伪直径

      这道题具有较强的欺骗性;一开始我没想明白,仿照求直径的树形dp设了两个状态:已经分叉过的路径和没有分叉过的路径;转移稍微有点复杂,但想起来很简单;就在我马上写完的时候,我突然想起两个路径可以互相包含...

      好的,先说结论,这道题的答案就是树的直径-1;分两步来证明:首先,这个值一定能取到,方法是一条路径选直径,另一条路径去掉直径端点处的某一条边;其次,这确实是可能存在的最大值,因为如果存在两条路径的交是直径,且他们不相同,那么取出它们不相交的部分接到直径上,会使直径变长,这显然不合理,所以答案就是树的直径-1;

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # define R register int
     4 
     5 using namespace std;
     6 
     7 const int N=200005;
     8 const int inf=1000000000;
     9 int n,h,x,y,firs[N],dep[N];
    10 int f1[N],f2[N],e[N];
    11 struct edge { int too,nex; }g[N<<1];
    12 
    13 void add (int x,int y)
    14 {
    15     g[++h].nex=firs[x];
    16     firs[x]=h;
    17     g[h].too=y;
    18 }
    19 
    20 void dfs (int x)
    21 {
    22     int j;
    23     for (R i=firs[x];i;i=g[i].nex)
    24     {
    25         j=g[i].too;
    26         if(dep[j]) continue;
    27         dep[j]=dep[x]+1;
    28         dfs(j);
    29         if(f1[j]+1>=f1[x]) f2[x]=f1[x],f1[x]=f1[j]+1,e[x]=j;
    30         else f2[x]=max(f2[x],f1[j]+1);
    31     }
    32 }
    33 
    34 void redfs (int x)
    35 {
    36     int j;
    37     for (R i=firs[x];i;i=g[i].nex)
    38     {
    39         j=g[i].too;
    40         if(dep[j]<dep[x]) continue;
    41         int t;
    42         if(e[x]==j) t=f2[x]+1;
    43         else t=f1[x]+1;
    44         if(t>=f1[j]) f2[j]=f1[j],f1[j]=t,e[j]=x;
    45         else f2[j]=max(f2[j],t); 
    46         redfs(j);
    47     }
    48 }
    49 
    50 int main()
    51 {
    52     int ans=0;
    53     scanf("%d",&n);
    54     for (R i=1;i<n;++i)
    55     {
    56         scanf("%d%d",&x,&y);
    57         add(x,y); add(y,x);
    58     }
    59     dep[1]=1; dfs(1);
    60     redfs(1);
    61     for (R i=1;i<=n;++i) ans=max(ans,f1[i]+f2[i]);
    62     printf("%d",ans-1);
    63     return 0;
    64 }
    E

    F 最大最小差

      一道有点难度的题;

      首先考虑一个暴力?$n^2$枚举区间,然后判断是否合理,不知道能得多少分?如果你认真思考一下这个过程,会发现很多枚举是无效的,举个例子:如果$(x,y)$的利润已经过高,那么$(x,y+1)$ 的利润只可能更高;再想一想,就会发现对于一个确定的 $x$ ,合法的右端点属于一个区间;我们是否可以通过某种方法直接找到这个区间呢?二分!怎么判断一个区间的利润呢?ST表查最大最小值!不过如果你每次同时处理 $m$ 种货物,$ST$表的空间可能就太大了,所以可以考虑对于每种货物分别计算合法的右端点,最后再对 $m$ 个区间求交;

      交上去,可能会TLE...因为ST表查询一次虽说是O(n)的,但是常数略大。有没有什么别的方法呢?发现随着枚举的做左端点不断增大,右端点的合法区间也是在不断右移的,所以可以用双指针维护这个合法区间,复杂度虽说还是 $O(nlog n)$的,但是这个 $log n$ 只是求 $ST$表时用到的,所以就可以通过了。

      赛后发现,这个最大最小值似乎可以通过单调队列维护,那么复杂度就是严格的 $O(nm)$ 啦!

      啊,之前放上来的是一个WA+TLE的程序(因为我最后一次提交是在牛客提交框里改的,电脑上的还是原来那版错误的),经QQ上dalao提醒,现在已经改过来了。不过我的做法有点卡常,加上超级快读才通过...

      
      1 # include <cstdio>
      2 # include <iostream>
      3 # define R register int
      4 # define ll long long
      5 # define getchar() (S==T&&(T=(S=BB)+fread(BB,1,1<<20,stdin),S==T)?EOF:*S++)
      6 char BB[1 << 20], *S = BB, *T = BB; 
      7   
      8 using namespace std;
      9 
     10 const int N=1000006;
     11 const int M=11;
     12 int n,m,a[N],c[M],lg[N];
     13 int st1[N][21],st2[N][21];
     14 int lr[N],rr[N];
     15 ll ans;
     16 
     17 void build_ST()
     18 {
     19     for (R i=1;i<=n;++i) st1[i][0]=st2[i][0]=a[i];
     20     for (R i=1;i<=20;++i)
     21         for (R j=1;j<=n;++j)
     22         {
     23             if(j+(1<<i)-1>n) break;
     24             st1[j][i]=min(st1[j][i-1],st1[j+(1<<(i-1))][i-1]);
     25             st2[j][i]=max(st2[j][i-1],st2[j+(1<<(i-1))][i-1]);
     26         }
     27 }
     28 
     29 int ask (int l,int r)
     30 {
     31     if(r>n) return -1;
     32     if(r<0) return -1;
     33     int k=lg[r-l+1];
     34     int ans1=min(st1[l][k],st1[ r-(1<<k)+1 ][k]);
     35     int ans2=max(st2[l][k],st2[ r-(1<<k)+1 ][k]);
     36     return ans2-ans1;
     37 }
     38 
     39 int ef1 (int id,int x)
     40 {
     41     int l=x,r=n,mid,ans=n+1;
     42     while(l<=r)
     43     {
     44         mid=(l+r)>>1;
     45         if(ask(x,mid)>=c[id]) ans=mid,r=mid-1;
     46         else l=mid+1;
     47     }
     48     return ans;
     49 }
     50 
     51 int ef2 (int id,int x)
     52 {
     53     int l=x,r=n,mid,ans=-n-1;
     54     while(l<=r)
     55     {
     56         mid=(l+r)>>1;
     57         if(ask(x,mid)<=c[id]) ans=mid,l=mid+1;
     58         else r=mid-1;
     59     }
     60     return ans;
     61 }
     62 
     63 int read()
     64 {
     65     int x=0;
     66     char c=getchar();
     67     while (!isdigit(c)) c=getchar();
     68     while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
     69     return x;
     70 }
     71 
     72 void solve (int id)
     73 {
     74     for (R i=1;i<=n;++i) a[i]=read();
     75     build_ST();
     76     int p1=ef1(id,1),p2=ef2(id,1);
     77     lr[1]=max(lr[1],p1); rr[1]=min(rr[1],p2);
     78     for (R i=2;i<=n;++i)
     79     {
     80         if(p1<i) p1=i; if(p2<i) p2=i;
     81         while(p1<=n&&ask(i,p1)<c[id]) p1++;
     82         while(p2+1<=n&&ask(i,p2+1)<=c[id]) p2++;
     83         if(ask(i,p1)!=c[id]) lr[i]=n+1; else lr[i]=max(lr[i],p1);
     84         if(ask(i,p2)!=c[id]) rr[i]=-n-1; else rr[i]=min(rr[i],p2);
     85     }
     86 }
     87 
     88 int main()
     89 {
     90     scanf("%d%d",&n,&m);
     91     for (R i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
     92     for (R i=1;i<=m;++i) c[i]=read();
     93     for (R i=1;i<=n;++i) rr[i]=n;
     94     for (R i=1;i<=m;++i)
     95         solve(i);
     96     for (R i=1;i<=n;++i)
     97     {
     98         int l=lr[i],r=rr[i];
     99         if(l>r) continue;
    100         ans+=r-l+1;
    101     }
    102     printf("%lld",ans);
    103     return 0;
    104 }
    F

      由于对赛制不是很确定,以为类似CF,担心会FST,又检查来检查去,后来问了监考老师,才知道过了就是过了,于是愉快的交卷。反正假都请好了,干脆在家度过了一个快乐的晚上。

      

    Update:

      我! 没! 了! 啊!

      这面试太太太太太太太难了吧!

      我! 自! 闭! 了!

      还没面试的同学不要再问我面试考啥了,因为真真真真的很随机啊啊啊啊啊!

  • 相关阅读:
    哈工大中文篇章关系语料
    MongoDB学习笔记~关于官方驱动集成IQueryable之后的一些事
    MongoDB学习笔记~为IMongoRepository接口更新指定字段
    MongoDB学习笔记系列
    MongoDB学习笔记~为IMongoRepository接口添加了增删改方法,针对官方驱动
    MongoDB学习笔记~为IMongoRepository接口添加了排序和表达式树,针对官方驱动
    Android NDK入门实例 计算斐波那契数列二生成.so库文件
    Spring Autowire自动装配
    在gem5的full system下运行 alpha编译的测试程序 running gem5 on ubuntu in full system mode in alpha
    工厂三兄弟之抽象工厂模式(二)
  • 原文地址:https://www.cnblogs.com/shzr/p/12018915.html
Copyright © 2020-2023  润新知