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

  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$ 都可以。

  

 # include <cstdio>
# include <iostream>
# define R register int using namespace std; int k; int main()
{
scanf("%d",&k);
printf("%d",k+);
return ;
}

A

B 吃桃

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

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

  

 # include <cstdio>
# include <iostream>
# define R register int using namespace std; const int N=;
int n,k,h,x,y,firs[N],dp[N],dep[N];
struct edge { int too,nex; }g[N<<]; void add (int x,int y)
{
g[++h].nex=firs[x];
firs[x]=h;
g[h].too=y;
} void dfs (int x)
{
int j;
for (R i=firs[x];i;i=g[i].nex)
{
j=g[i].too;
if(dep[j]) continue;
dep[j]=dep[x]+;
dfs(j);
dp[x]=max(dp[x],dp[j]);
}
dp[x]++;
} void redfs (int x)
{
printf("%d\n",x);
if(dp[x]==) return;
int j,ans=n+;
for (R i=firs[x];i;i=g[i].nex)
{
j=g[i].too;
if(dep[j]<dep[x]) continue;
if(dp[j]+==dp[x]) ans=min(ans,j);
}
redfs(ans);
} int main()
{
scanf("%d%d",&n,&k);
for (R i=;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dep[k]=; dfs(k);
redfs(k);
return ;
}

B

C 背包

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

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

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

  

 # include <cstdio>
# include <iostream>
# define R register int using namespace std; const int N=;
const int inf=;
int n,V,v[N],w[N],dp[];
int ans=inf; int main()
{
scanf("%d%d",&n,&V);
for (R i=;i<=n;++i)
scanf("%d%d",&v[i],&w[i]);
for (R i=;i<=V;++i) dp[i]=inf;
for (R i=;i<=n;++i)
{
for (R j=V-v[i];j<V;++j)
ans=min(ans,dp[j]+w[i]);
if(v[i]>=V) ans=min(ans,w[i]);
for (R j=V;j>=v[i];--j)
dp[j]=min(dp[j],dp[ j-v[i] ]+w[i]);
}
ans=min(ans,dp[V]);
printf("%d",ans);
return ;
}

C

D 泡面

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

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

  

 # include <cstdio>
# include <iostream>
# include <algorithm>
# include <queue>
# define R register int
# define ll long long using namespace std; const int N=;
int n,p,h,s;
ll ans[N],T;
struct peo
{
int pos,t;
}a[N];
priority_queue <int,vector<int>,greater<int> > q; bool cmp (peo a,peo b)
{
if(a.t==b.t) return a.pos<b.pos;
return a.t<b.t;
} int read()
{
int x=;
char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) x=(x<<)+(x<<)+(c^),c=getchar();
return x;
} int main()
{
scanf("%d%d",&n,&p);
for (R i=;i<=n;++i)
a[i].pos=i,a[i].t=read();
sort(a+,a++n,cmp);
a[n+].t=;
h=; T=a[].t+p; ans[ a[].pos ]=T;
int cg=;
while(cg!=n)
{
while(h+<=n&&a[h+].t<=T) q.push(a[h+].pos),h++;
int s=q.size();
if(s==)
{
h++;
ans[ a[h].pos ]=a[h].t+p;
cg++;
T=a[h].t+p;
continue;
}
int x=q.top(); q.pop();
T+=p; ans[x]=T; cg++;
}
for (R i=;i<=n;++i) printf("%lld ",ans[i]);
return ;
}

D

E 伪直径

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

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

  

 # include <cstdio>
# include <iostream>
# define R register int using namespace std; const int N=;
const int inf=;
int n,h,x,y,firs[N],dep[N];
int f1[N],f2[N],e[N];
struct edge { int too,nex; }g[N<<]; void add (int x,int y)
{
g[++h].nex=firs[x];
firs[x]=h;
g[h].too=y;
} void dfs (int x)
{
int j;
for (R i=firs[x];i;i=g[i].nex)
{
j=g[i].too;
if(dep[j]) continue;
dep[j]=dep[x]+;
dfs(j);
if(f1[j]+>=f1[x]) f2[x]=f1[x],f1[x]=f1[j]+,e[x]=j;
else f2[x]=max(f2[x],f1[j]+);
}
} void redfs (int x)
{
int j;
for (R i=firs[x];i;i=g[i].nex)
{
j=g[i].too;
if(dep[j]<dep[x]) continue;
int t;
if(e[x]==j) t=f2[x]+;
else t=f1[x]+;
if(t>=f1[j]) f2[j]=f1[j],f1[j]=t,e[j]=x;
else f2[j]=max(f2[j],t);
redfs(j);
}
} int main()
{
int ans=;
scanf("%d",&n);
for (R i=;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dep[]=; dfs();
redfs();
for (R i=;i<=n;++i) ans=max(ans,f1[i]+f2[i]);
printf("%d",ans-);
return ;
}

E

F 最大最小差

  一道有点难度的题;

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

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

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

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

  

 # include <cstdio>
# include <iostream>
# define R register int
# define ll long long
# define getchar() (S==T&&(T=(S=BB)+fread(BB,,<<,stdin),S==T)?EOF:*S++)
char BB[ << ], *S = BB, *T = BB; using namespace std; const int N=;
const int M=;
int n,m,a[N],c[M],lg[N];
int st1[N][],st2[N][];
int lr[N],rr[N];
ll ans; void build_ST()
{
for (R i=;i<=n;++i) st1[i][]=st2[i][]=a[i];
for (R i=;i<=;++i)
for (R j=;j<=n;++j)
{
if(j+(<<i)->n) break;
st1[j][i]=min(st1[j][i-],st1[j+(<<(i-))][i-]);
st2[j][i]=max(st2[j][i-],st2[j+(<<(i-))][i-]);
}
} int ask (int l,int r)
{
if(r>n) return -;
if(r<) return -;
int k=lg[r-l+];
int ans1=min(st1[l][k],st1[ r-(<<k)+ ][k]);
int ans2=max(st2[l][k],st2[ r-(<<k)+ ][k]);
return ans2-ans1;
} int ef1 (int id,int x)
{
int l=x,r=n,mid,ans=n+;
while(l<=r)
{
mid=(l+r)>>;
if(ask(x,mid)>=c[id]) ans=mid,r=mid-;
else l=mid+;
}
return ans;
} int ef2 (int id,int x)
{
int l=x,r=n,mid,ans=-n-;
while(l<=r)
{
mid=(l+r)>>;
if(ask(x,mid)<=c[id]) ans=mid,l=mid+;
else r=mid-;
}
return ans;
} int read()
{
int x=;
char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) x=(x<<)+(x<<)+(c^),c=getchar();
return x;
} void solve (int id)
{
for (R i=;i<=n;++i) a[i]=read();
build_ST();
int p1=ef1(id,),p2=ef2(id,);
lr[]=max(lr[],p1); rr[]=min(rr[],p2);
for (R i=;i<=n;++i)
{
if(p1<i) p1=i; if(p2<i) p2=i;
while(p1<=n&&ask(i,p1)<c[id]) p1++;
while(p2+<=n&&ask(i,p2+)<=c[id]) p2++;
if(ask(i,p1)!=c[id]) lr[i]=n+; else lr[i]=max(lr[i],p1);
if(ask(i,p2)!=c[id]) rr[i]=-n-; else rr[i]=min(rr[i],p2);
}
} int main()
{
scanf("%d%d",&n,&m);
for (R i=;i<=n;++i) lg[i]=lg[i>>]+;
for (R i=;i<=m;++i) c[i]=read();
for (R i=;i<=n;++i) rr[i]=n;
for (R i=;i<=m;++i)
solve(i);
for (R i=;i<=n;++i)
{
int l=lr[i],r=rr[i];
if(l>r) continue;
ans+=r-l+;
}
printf("%lld",ans);
return ;
}

F

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

  

Update:

  我! 没! 了! 啊!

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

  我! 自! 闭! 了!

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

2019-2020Nowcoder Girl初赛题解的更多相关文章

  1. 华南师大 2017 年 ACM 程序设计竞赛新生初赛题解

    题解 被你们虐了千百遍的题目和 OJ 也很累的,也想要休息,所以你们别想了,行行好放过它们,我们来看题解吧... A. 诡异的计数法 Description cgy 太喜欢质数了以至于他计数也需要用质 ...

  2. Comet OJ 2019 夏季欢乐赛题解

    Comet OJ 2019 夏季欢乐赛题解 我是来骗访问量的 A 完全k叉树 \(n\)个点的完全k叉树的直径. 直接做 B 距离产生美 直接做 C 烤面包片 \(n!!!\mod p\) 显然\(n ...

  3. [NOIP2018]普及组初赛题解

    老师布置的作业,借博客这个平台一用 [总体感觉]对我而言比去年的难度大……特别是最后一题. 选择题 1.D 打印机属于输出设备 2.D 将全部进制转换为10进制进行对比,我的方法是每一位乘以进制的位数 ...

  4. 洛谷P5283 & LOJ3048:[十二省联考2019]异或粽子——题解

    https://www.luogu.org/problemnew/show/P5283 https://loj.ac/problem/3048 小粽是一个喜欢吃粽子的好孩子.今天她在家里自己做起了粽子 ...

  5. kick start 2019 round D T3题解

    ---恢复内容开始--- 题目大意:共有N个房子,每个房子都有各自的坐标X[i],占据每个房子需要一定花费C[i].现在需要选择K个房子作为仓库,1个房子作为商店(与题目不同,概念一样),由于仓库到房 ...

  6. kick start 2019 round D T2题解

    题目大意:由N个房子围成一个环,G个人分别顺时针/逆时针在房子上走,一共走M分钟,每分钟结束,每个人顺/逆时针走到相邻的房子.对于每个房子都会记录最后时刻到达的人(可能是一群人).最终输出每个人会被几 ...

  7. 2019.11.11&12题解

    Day1 考的不是很好,T1T2没区分度,T3想的太少,考试后期几乎都是在摸鱼,bitset乱搞也不敢打,只拿到了35分,跟前面的差距很大 A. 最大或 标签: 二进制+贪心 题解: 首先x,y中一定 ...

  8. 2019.11.12&13题解

    写在前面: 虽然拿到了rk1,但是T3被卡常TLE90分,(考后再交就A了!?),lemon80,又丢失了一次良好的AK机会, 掐头去尾距离联赛仅剩2天,最近中午一直睡不好,可能是有些紧张, 希望自己 ...

  9. The Preliminary Contest for ICPC Asia Nanjing 2019/2019南京网络赛——题解

    (施工中……已更新DF) 比赛传送门:https://www.jisuanke.com/contest/3004 D. Robots(期望dp) 题意 给一个DAG,保证入度为$0$的点只有$1$,出 ...

随机推荐

  1. SQL事务回滚

    BEGIN TRAN标记事务开始 COMMIT TRAN 提交事务 一般把DML语句(select ,delete,update,insert语句)放在BEGIN TRAN...COMMIT TRAN ...

  2. LeetCode 23. 合并K个排序链表(Merge k Sorted Lists)

    题目描述 合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [   1->4->5,   1->3->4,   2->6 ] 输出: ...

  3. SpringSecurity remember-me功能

    1./login .and().formLogin().loginPage("/user/login.html") //在successHandler中,使用response返回登 ...

  4. java list对象按照某个属性去重

    /** * 去重 * * @param orderList * @return * @author jqlin */ private static List<ansVo> removeDu ...

  5. HTML头信息标签和标题标签

    <html> <!-- 头信息的作用 1. 可以设置网页的标题. 2. 可以通知浏览使用指定的码表解释html页面. --> <head> <meta htt ...

  6. DEDECMS 漏洞汇总

    日期:2019-08-08 10:20:28 更新: 作者:Bay0net 介绍: 0x01.组合拳拿 shell 漏洞版本:v5.5 - v5.7 前台任意用户密码重置 首先注册一个账户,账户名为 ...

  7. IPTV系统的VOD与TV业务性能测试

    IPTV的未来发展正在成为业界的焦点话题.据市场研究公司MRG的统计,全球IPTV用户将由2004年的200万增加至2010年的2000万,预计全球IPTV市场2005-2010年的复合增长率为102 ...

  8. Azure sql database 监控存储过程的传参情况

    背景 实施开发的同事找到我,反馈说项目中使用Azure sql database 之后,无法使用Profiler来监控自己开发的存储过程的参数传参情况.确实profiler这些实例级别的工具在Azur ...

  9. linux常用命令(16)locate命令

    locate 让使用者可以很快速的搜寻档案系统内是否有指定的档案.其方法是先建立一个包括系统内所有档案名称及路径的数据库,之后当寻找时就只需查询这个数据库,而不必实际深入档案系统之中了.在一般的 di ...

  10. C基础知识(12):可变参数

    该功能需要使用<stdarg.h>.函数的最后一个参数写成省略号,即三个点号(...),省略号之前的那个参数是int,代表了要传递的可变参数的总数.该文件提供了实现可变参数功能的函数和宏. ...