多校A层冲刺NOIP2024模拟赛21
\(T1\) A. 送信卒 \(90pts/100pts\)
-
部分分
-
\(90pts\)
-
设最后的可能的最短路中左右共移动了 \(d\) 次,上下共移动了 \(x\) 次。
-
则等价于求 \(\min \{ x_{i}k+d_{i} \}=s\) 的解,观察到 \(d \in [0,\min(\left\lceil \frac{nm}{2} \right\rceil,s)]\) 。
-
将左右移动次数也扔进 \(Dijkstra\) 中转移即可,然后暴力进行 \(check\) ,时间复杂度为 \(O(n^{4}\log n)\) 。
-
因为需要 \(O(n^{4})\) 的辅助空间,所以需要开
short
。点击查看代码
const double eps=1e-8; struct node {int nxt,to,x,d; }e[40010]; int head[10010],cnt=0,n,m,limit; short dis[10010][5010]; bitset<5010>vis[10010]; char c[110][110]; struct quality {short dis;int x,d;bool operator < (const quality &another) const{return dis>another.dis;} }; void add(int u,int v,int x,int d) {cnt++;e[cnt].nxt=head[u];e[cnt].to=v;e[cnt].x=x;e[cnt].d=d;head[u]=cnt; } int work(int x,int y) {return (x-1)*m+y; } void dijkstra(int s) {memset(dis,0x3f,sizeof(dis));priority_queue<quality>q;dis[s][0]=0;q.push((quality){dis[s][0],s,0});while(q.empty()==0){int x=q.top().x,d=q.top().d;q.pop();if(vis[x][d]==0){vis[x][d]=1;for(int i=head[x];i!=0;i=e[i].nxt){if(d+e[i].d<=limit&&dis[e[i].to][d+e[i].d]>dis[x][d]+e[i].x){dis[e[i].to][d+e[i].d]=dis[x][d]+e[i].x;q.push((quality){dis[e[i].to][d+e[i].d],e[i].to,d+e[i].d});}}}} } int main() { #define Issac #ifdef Issacfreopen("msg.in","r",stdin);freopen("msg.out","w",stdout); #endifint sx,sy,tx,ty,t,i,j;double s,ans=0x7f7f7f7f,k,minn;scanf("%d%d%d%d%d%d",&n,&m,&sx,&sy,&tx,&ty);t=work(tx,ty);for(i=1;i<=n;i++){for(j=1;j<=m;j++){scanf(" %c",&c[i][j]);}}cin>>s;limit=min(ceil(n*m/2.0),ceil(s)-1);for(i=1;i<=n;i++){for(j=1;j<=m;j++){if(c[i][j]=='0'){if(i-1>=0&&c[i-1][j]=='0'){add(work(i,j),work(i-1,j),1,0);}if(i+1<=n&&c[i+1][j]=='0'){add(work(i,j),work(i+1,j),1,0);}if(j-1>=0&&c[i][j-1]=='0'){add(work(i,j),work(i,j-1),0,1);}if(j+1<=m&&c[i][j+1]=='0'){add(work(i,j),work(i,j+1),0,1);}}}}dijkstra(work(sx,sy));if((int)s==s&&tx==sx&&abs(ty-sy)==(int)s){ans=0;}else{for(i=0;i<=limit;i++){if(dis[t][i]!=0x3f3f&&dis[t][i]!=0){k=1.0*(s-i)/dis[t][i];minn=0x7f7f7f7f;for(j=0;j<=limit;j++){if(minn-(1.0*j+1.0*k*dis[t][j])>eps){minn=1.0*j+1.0*k*dis[t][j];}}if(fabs(minn-s)<=eps&&ans-k>eps){ans=k;}}}}printf("%.3lf\n",ans);return 0; }
-
-
\(100pts\) :将上述做法的 \(dijsktra\) 改成 \(01BFS\) 即可,时间复杂度为 \(O(n^{4})\) 。
点击查看代码
const double eps=1e-8; struct node {int nxt,to,x,d; }e[40010]; int head[10010],cnt=0,n,m,limit; short dis[10010][5010]; bitset<5010>vis[10010]; char c[110][110]; void add(int u,int v,int x,int d) {cnt++;e[cnt].nxt=head[u];e[cnt].to=v;e[cnt].x=x;e[cnt].d=d;head[u]=cnt; } int work(int x,int y) {return (x-1)*m+y; } void bfs(int s) {memset(dis,0x3f,sizeof(dis));deque<pair<int,int> >q;dis[s][0]=0;q.push_back(make_pair(s,0));while(q.empty()==0){int x=q.front().first,d=q.front().second;q.pop_front();if(vis[x][d]==0){vis[x][d]=1;for(int i=head[x];i!=0;i=e[i].nxt){if(d+e[i].d<=limit&&dis[e[i].to][d+e[i].d]>dis[x][d]+e[i].x){dis[e[i].to][d+e[i].d]=dis[x][d]+e[i].x;if(e[i].x==1){q.push_back(make_pair(e[i].to,d+e[i].d));} else{q.push_front(make_pair(e[i].to,d+e[i].d));}}}}} } int main() { #define Issac #ifdef Issacfreopen("msg.in","r",stdin);freopen("msg.out","w",stdout); #endifint sx,sy,tx,ty,t,i,j;double s,ans=0x7f7f7f7f,k,minn;scanf("%d%d%d%d%d%d",&n,&m,&sx,&sy,&tx,&ty);t=work(tx,ty);for(i=1;i<=n;i++){for(j=1;j<=m;j++){scanf(" %c",&c[i][j]);}}cin>>s;limit=min(ceil(n*m/2.0),ceil(s)-1);for(i=1;i<=n;i++){for(j=1;j<=m;j++){if(c[i][j]=='0'){if(i-1>=0&&c[i-1][j]=='0'){add(work(i,j),work(i-1,j),1,0);}if(i+1<=n&&c[i+1][j]=='0'){add(work(i,j),work(i+1,j),1,0);}if(j-1>=0&&c[i][j-1]=='0'){add(work(i,j),work(i,j-1),0,1);}if(j+1<=m&&c[i][j+1]=='0'){add(work(i,j),work(i,j+1),0,1);}}}}bfs(work(sx,sy));if((int)s==s&&tx==sx&&abs(ty-sy)==(int)s){ans=0;}else{for(i=0;i<=limit;i++){if(dis[t][i]!=0x3f3f&&dis[t][i]!=0){k=1.0*(s-i)/dis[t][i];minn=0x7f7f7f7f;for(j=0;j<=limit;j++){if(minn-(1.0*j+1.0*k*dis[t][j])>eps){minn=1.0*j+1.0*k*dis[t][j];}}if(fabs(minn-s)<=eps&&ans-k>eps){ans=k;}}}}printf("%.3lf\n",ans);return 0; }
-
-
正解
- 观察到 \(\min \{ x_{i}k+d_{i} \}\) 具有单调性,即随着 \(k\) 的增大最短路长度不降。
- 二分答案即可。
点击查看代码
const double eps=1e-8; double dis[110][110]; bool vis[110][110]; char c[110][110]; void dijkstra(int sx,int sy,double mid,int n,int m) {for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){vis[i][j]=0;dis[i][j]=0x3f3f3f3f;}}priority_queue<pair<int,pair<int,int>>>q;dis[sx][sy]=0;q.push(make_pair(-dis[sx][sy],make_pair(sx,sy)));while(q.empty()==0){int x=q.top().second.first,y=q.top().second.second;q.pop();if(vis[x][y]==0){vis[x][y]=1;if(x-1>=1&&dis[x-1][y]>dis[x][y]+mid&&c[x-1][y]=='0'){dis[x-1][y]=dis[x][y]+mid;q.push(make_pair(-dis[x-1][y],make_pair(x-1,y)));}if(x+1<=n&&dis[x+1][y]>dis[x][y]+mid&&c[x+1][y]=='0'){dis[x+1][y]=dis[x][y]+mid;q.push(make_pair(-dis[x+1][y],make_pair(x+1,y)));}if(y-1>=1&&dis[x][y-1]>dis[x][y]+1&&c[x][y-1]=='0'){dis[x][y-1]=dis[x][y]+1;q.push(make_pair(-dis[x][y-1],make_pair(x,y-1)));}if(y+1<=m&&dis[x][y+1]>dis[x][y]+1&&c[x][y+1]=='0'){dis[x][y+1]=dis[x][y]+1;q.push(make_pair(-dis[x][y+1],make_pair(x,y+1)));}}} } int main() { #define Issac #ifdef Issacfreopen("msg.in","r",stdin);freopen("msg.out","w",stdout); #endifint n,m,sx,sy,tx,ty,i,j;double s,l=0,r,mid,ans=-1;cin>>n>>m>>sx>>sy>>tx>>ty;for(i=1;i<=n;i++){for(j=1;j<=m;j++){cin>>c[i][j];}}cin>>s;r=s;while(r-l>=eps){mid=(l+r)/2;dijkstra(sx,sy,mid,n,m);if(dis[tx][ty]>=s){ans=mid;r=mid;}else{l=mid;}}printf("%.3lf\n",ans);return 0; }
\(T2\) B. 共轭树图 \(0pts/0pts\)
-
不妨钦定 \(u>v\) ,那么在断开 \((u,v)\) 这条边后等价于将 \(u\) 的深度最浅的祖先和 \(v\) 连接。故最终得到的 \(G\) 也一定是棵树,且当以 \(n\) 为根时也满足父亲节点的编号一定大于自身的编号。
-
考虑这样的 \(G\) 是怎么构造出来的。当把 \(G\) 的边在原图上画出后,任意两条边之间只能不交和包含,因为如果相交的话下边的那条边的端点就可以连到上面的边上去。
-
定义根节点的深度为 \(1\) 。
-
设 \(f_{x,i}\) 表示 \(x\) 在 \(G\) 中只被允许与原图中它的 \(i \in [1,dep_{x}-1]\) 个祖先连边时(即 \(G\) 中的父亲节点)以 \(x\) 为根的子树中的方案数,状态转移方程为 \(f_{x,i}=\sum\limits_{j=2}^{i+1}\prod\limits_{y \in Son(x)}f_{y,j}\) ,边界为 \(f_{x,i}=i(i \in [1,dep_{x}-1] \land du_{x}=1)\) 。
-
此时时间复杂度为 \(O(n^{3})\) ,考虑进一步优化。
-
手摸展开后的式子,容易有 \(f_{x,i}=f_{x,i-1}+\prod\limits_{y \in Son(x)}f_{y,i+1}\) 。此时时间复杂度就优化成了 \(O(n^{2})\) 。
-
最终,有 \(\prod\limits_{x \in Son(n)}f_{x,1}\) 即为所求。
点击查看代码
const ll p=998244353; int dep[3010],f[3010][3010]; vector<int>e[3010]; void add(int u,int v) {e[u].push_back(v); } void dfs(int x,int fa) {dep[x]=dep[fa]+1;for(int i=0;i<e[x].size();i++){dfs(e[x][i],x);}for(int k=1;k<=dep[x]-1;k++){f[x][k]=1;for(int i=0;i<e[x].size();i++){f[x][k]=1ll*f[x][k]*f[e[x][i]][k+1]%p;}f[x][k]=(f[x][k]+f[x][k-1])%p;} } int main() { #define Issac #ifdef Issacfreopen("reflection.in","r",stdin);freopen("reflection.out","w",stdout); #endifint n,u,v,ans=1,i;cin>>n;for(i=1;i<=n-1;i++){cin>>u>>v;if(u<v){swap(u,v);}add(u,v);}dfs(n,0);for(i=0;i<e[n].size();i++) {ans=1ll*ans*f[e[n][i]][1]%p;}cout<<ans<<endl;return 0; }
\(T3\) C. 摸鱼军训 \(0pts/0pts\)
- 部分分
- \(20 \%\) : \(O(n^{2})\) 预处理 \(O(1)\) 查询。
\(T4\) D. 神奇园艺师 \(0pts/0pts\)
- 部分分
- 子任务 \(1\) :爆搜子集后就是货仓选址问题了,暴力分解质因数即可。
总结
- \(T1\) 赛时觉得因为 \(k\) 的变化会导致最短路路径的变化然后就不能确定最短路长度了,没搞清其内部的单调性。
- \(T2\) 始终没读懂题意。
- \(T3\) 忘了可以 \(O(n^{2})\) 预处理,最低档部分分只写了单组询问 \(O(n^{2})\) 的做法,挂了 \(20pts\) 。
- \(T4\) 质因数分解写的是预处理素数挨个分解的方法,导致无用素数极多,挂了 \(20pts\) 。