NOIP2024 前集训:多校A层冲刺NOIP2024模拟赛21
前言
rk 历程:\(11\to 9\to 8\to 7\),原因是部分人的 T1 假做法被卡(感觉目前这些不二分也不 DP(特指 Hangry 这样的 \(O(n^4)\) DP)都能卡)。
T2 建图建错了(没判谁是父亲也没建双向边)暴力死了,挂了 \(24\)。
我是唯一一个打了 T3 启发正解部分分但没有写出正解的人……
T4 部分分都不会,润了。
T1 送信卒
单调性过于显然,所以直接二分答案即可,check 直接写 dijkstra,复杂度 \(O(m\log m\log n)\),因为是 double 所以要写 eps。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=110,M=1e4+10; const double eps=1e-8;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,sx,sy,tx,ty,tot,head[M],nxt[M<<2],to[M<<2];
bool w[M<<2],vis[M],a[N][N]; double s,l,r=1e5,mid,dis[M];
struct aa
{
int pos; double dis;
inline bool operator < (const aa a) const {return dis-a.dis>eps;}
}; priority_queue<aa>q;
inline void add(int x,int y,int z)
{nxt[++tot]=head[x],to[tot]=y,w[tot]=z,head[x]=tot;}
inline int calc(int x,int y) {return (x-1)*m+y;}
inline bool check(int x,int y) {return x>=1&&x<=n&&y>=1&&y<=m&&!a[x][y];}
inline double calc(int x) {return x?mid:1;}
inline bool check()
{
fill(dis+1,dis+1+n*m,1e9),fill(vis+1,vis+1+n*m,0);
for(q.push({calc(sx,sy),dis[calc(sx,sy)]=0});!q.empty();)
{
int x=q.top().pos; q.pop(); if(vis[x]) continue; vis[x]=1;
for(int i=head[x],y;y=to[i];i=nxt[i])
if(dis[y]-dis[x]-calc(w[i])>eps)
dis[y]=dis[x]+calc(w[i]),q.push({y,dis[y]});
}
return dis[calc(tx,ty)]-s>=eps;
}
signed main()
{
freopen("msg.in","r",stdin),freopen("msg.out","w",stdout);
read(n,m,sx,sy,tx,ty);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) read(a[i][j]);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(!a[i][j])
{
if(check(i,j-1)) add(calc(i,j),calc(i,j-1),0);
if(check(i,j+1)) add(calc(i,j),calc(i,j+1),0);
if(check(i-1,j)) add(calc(i,j),calc(i-1,j),1);
if(check(i+1,j)) add(calc(i,j),calc(i+1,j),1);
}
for(scanf("%lf",&s);r-l>eps;) mid=(l+r)/2.0,check()?r=mid:l=mid;
printf("%.3lf",l);
}
T2 共轭树图
对于每一个点钦定一个 \(dep\),那么 \(x\) 在生成树中的深度可以为 \([1,dep_x]\),既可以和与他联通的任意一个祖先连边。
设 \(f_{x,i}\) 表示点 \(x\) 向上连深度不超过 \(i\) 的祖先的方案数,这本质上是一个前缀和优化,那么有:
\[f_{x,i}=\prod_{y\in son_x}\sum_{j=1}^if_{y,j+1}
\]
复杂度 \(O(n^2)\)。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define pb push_back
using namespace std;
const int N=3e3+10,P=998244353;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,f[N][N]; ll ans=1; vector<int>e[N];
inline int mod(int x,int y) {return (x+=y)>=P?x-P:x;}
inline void dfs(int x,int dep)
{
for(int y:e[x]) dfs(y,dep+1); f[x][1]=1;
for(int i=1;i<=dep;f[x][i]=mod(f[x][i],f[x][i-1]),f[x][++i]=1)
for(int y:e[x]) f[x][i]=1ll*f[x][i]*f[y][i+1]%P;
}
signed main()
{
freopen("reflection.in","r",stdin),freopen("reflection.out","w",stdout);
read(n); for(int i=1,x,y;i<n;i++) read(x,y),x<y?e[y].pb(x):e[x].pb(y);
dfs(n,0); for(int i:e[n]) (ans*=f[i][1])%=P; write(ans);
}
T3 摸鱼军训
考虑对于每个点单独考虑,定义 \(pre_i\) 表示 \(i\) 这个点前面有多少个 \(>i\) 的点,那么若 \(k<pre_i\),答案就是 \(pre_i-k\),现在需要考虑 \(k>pre_i\) 的怎么做,这是可以用树状数组或线段树维护的。
发现每次操作 \(i\) 会停到后面第一个 \(>i\) 的位置的前一个位置,那么问题就迎刃而解了,找到第一个前缀中 \(>i\) 的的数量 \(=k\) 的位置 \(pos\),那么答案就是 \(pos-k\),这是可以在值域线段树上二分维护的。
关于我赛时想到结论但是不知道用数据结构维护这件事。
T4 神奇园艺师
并不想调,咕了。