2024.7.29模拟赛11
模拟赛
马上要补到八月啦!!!
T1 FATE
\(1 \to n\) 最短路长度为 \(d\),求 \(1 \to n\) 长度为 \(d+1\) 的路径数量。
还挺水。
首先求出最短路长度,然后从 \(n\) 开始搜,考虑只可能是某一条最短路多走一步,所以标记是否多走了这一步,其他的全部走最短路就好啦。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5,mod = 1e9+7;
#define LL long long
int n,m;
int s,t;
int head[N],tot;
LL f[N][2];
struct E {int u,v;} e[N<<1];
inline void add(int u,int v) {e[++tot]={head[u],v}; head[u]=tot;}
int d[N];
bool vs[N];
void dj(int s)
{
memset(d,0x3f,sizeof(d));
priority_queue<pair<int,int> > q;
d[s]=0; q.push(make_pair(0,s));
while(!q.empty())
{
int u=q.top().second; q.pop();
if(vs[u]) continue;
vs[u]=1;
for(int i=head[u];i;i=e[i].u)
{
int v=e[i].v;
if(!vs[v]&&d[v]>d[u]+1)
{
d[v]=d[u]+1;
q.push(make_pair(-d[v],v));
}
}
}
}
LL dfs(int u,int k)
{
if(f[u][k]) return f[u][k];
if(u==s) return (k==0);
LL res=0;
for(int i=head[u];i;i=e[i].u)
{
int v=e[i].v;
if(k&&d[v]==d[u]) res=(res+dfs(v,0))%mod;
else if(d[v]==d[u]-1) res=(res+dfs(v,k))%mod;
}
return f[u][k]=res;
}
int main()
{
// freopen("Fate9.in","r",stdin);
// freopen("out.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++)
{
int x,y; scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dj(s);
printf("%lld\n",dfs(t,1));
return 0;
}
T2 Fishing
思维题吧,好像能用模拟退火骗分。
考虑鱼之间的相对运动,每次定住一条鱼,令网的最左端在这条鱼上,其他鱼相对运动。网一定在某个时刻鱼刚好出边界或进边界时最优,都是 \(O(n)\) 的,一共 \(O(n^2)\)。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e3+5;
const double inf=1e-9;
#define LL long long
int n,a,w[N],x[N],v[N],ans;
map<double,int> mp;
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
scanf("%d%d",&n,&a);
for(int i=1;i<=n;i++) scanf("%d%d%d",&w[i],&x[i],&v[i]);
for(int i=1;i<=n;i++)
{
int res=w[i]; mp.clear();
for(int j=1;j<=n;j++)
{
if(j==i) continue;
if(v[i]==v[j])
{if(x[j]>=x[i]&&x[j]<=x[i]+a) res+=w[j];}
else
{
double tl=1.0*(x[i]-x[j])/(v[j]-v[i]);
double tr=1.0*(x[i]+a-x[j])/(v[j]-v[i]);
if(tr-tl<inf) swap(tl,tr);
if(tr>=0)
{
tl=max(0.0,tl);
mp[tl]+=w[j];
mp[tr+inf]-=w[j];
}
}
}
ans=max(ans,res);
if(!mp.empty())
for(pair<double,int> i:mp)
res+=i.second,ans=max(ans,res);
}
printf("%d\n",ans);
return 0;
}
T3 ≥ K
非常巧妙的一道题,我们考虑插空法。
首先排序,左右指针从两端开始扫。
对于一个较小的数,它必须放在两个大于等于 \(K-a_i\) 的数之间,
所以我们右指针扫,每扫到一个大于等于的数会将可填的位置 \(+1\),扫到不能扫的时候把刚才那个较小的数填进去,并将可填的位置 \(-1\)。
每次统计方案数就好啦。
code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 2e5+5,mod = 998244353;
int n,k,a[N],cnt[N];
LL ans,p[N];
LL qpow(LL a,int b)
{
LL res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod; b>>=1;
}
return res;
}
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
scanf("%d%d",&n,&k);
p[1]=1; for(int i=2;i<=n;i++) p[i]=p[i-1]*i%mod;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);
int l=1,r=n,t=1; ans=1;
while(l<=r)
{
while(1ll*a[l]+a[r]>=1ll*k&&l<r) ans=(ans*t)%mod,r--,t++;
ans=(ans*t)%mod; t--; l++;
}
// printf("%lld\n",ans);
for(int i=1,j=1;i<=n;j=i)
{
while(a[j]==a[j+1]) j++;
cnt[++cnt[0]]=j-i+1; i=j+1;
}
for(int i=1;i<=cnt[0];i++)
{
if(cnt[i]>1)
ans=(ans*qpow(p[cnt[i]],mod-2))%mod;
}
printf("%lld\n",ans);
return 0;
}
T4 Tourism
珂朵莉树。
转化题意,给出 \(m\) 个关键点。每次查询包含点 \(a_l \dots a_r\) 的最小子图大小。
首先树剖把树上问题转化为区间问题,然后考虑如何维护区间信息。
对于子图大小,我们可以用染色的方式,每次将两点间的路径染成同一颜色(用更新时间作为颜色),设第 \(i\) 次更新的点数为 \(s_i\),初始时 \(s_1=n\)。
这样随右端点的右移,每次将 \(a_{i-1}\) 和 \(a_i\) 的路径染成当前时间。
当我们染到 \(a_r\) 时,最小子图大小就是 \(n-\sum_{i=1}^ls_i\)(必须连的都已经在 \([a_l,a_r]\) 之间了,剩下的没用,大于 \(a_r\) 的还没更新到),类似前缀和?
染色就是维护颜色段区间推平操作,用珂朵莉维护。(码量还挺小)
code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
#define fi first
#define se second
int n,m,q,a[N],ans[N];
int head[N],tot;
struct E {int u,v;} e[N<<1];
inline void add(int u,int v) {e[++tot]={head[u],v}; head[u]=tot;}
vector<pair<int,int> > Q[N];
struct BIT
{
int c[N];
inline void mdf(int x,int v) {for(;x<=m;x+=(x&-x)) c[x]+=v;}
inline int que(int x) {int res=0; for(;x;x-=(x&-x)) res+=c[x]; return res;}
} bit;
struct ODT
{
struct node
{
int l,r; mutable int v;
bool operator <(const node &x) const {return l<x.l;}
}; set<node> s;
inline auto get(int x)
{
auto it=s.upper_bound({x,N,0}); it--;
if(it->l==x) return it;
int l=it->l,r=it->r,v=it->v;
s.erase(it),s.insert({l,x-1,v});
return s.insert({x,r,v}).first;
}
inline void change(int l,int r,int v)
{
auto end=get(r+1),begin=get(l);
for(auto it=begin;it!=end;++it)
bit.mdf(it->v,-(it->r-it->l+1));
s.erase(begin,end);
s.insert({l,r,v}),bit.mdf(v,r-l+1);
}
} odt;
namespace TCS
{
int dep[N],sz[N],son[N],dfn[N],cnt,rk[N],fa[N],top[N];
void dfs1(int u,int f)
{
dep[u]=dep[f]+1; fa[u]=f; son[u]=-1; sz[u]=1;
for(int i=head[u];i;i=e[i].u)
{
int v=e[i].v; if(v==f) continue;
dfs1(v,u); sz[u]+=sz[v];
if(son[u]==-1||sz[son[u]]<sz[v]) son[u]=v;
}
}
void dfs2(int u,int t)
{
top[u]=t; dfn[u]=++cnt; rk[cnt]=u;
if(son[u]==-1) return;
dfs2(son[u],t);
for(int i=head[u];i;i=e[i].u)
{
int v=e[i].v;
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
}
void change(int x,int y,int t)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
odt.change(dfn[top[x]],dfn[x],t);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
odt.change(dfn[x],dfn[y],t);
}
}; using namespace TCS;
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
odt.s.insert({1,n,1}); bit.mdf(1,n);
for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs1(1,0); dfs2(1,1);
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
for(int i=1,l,r;i<=q;i++)
{
scanf("%d%d",&l,&r);
if(l==r) ans[i]=1; else Q[r].push_back({l,i});
}
for(int i=2;i<=m;i++)
{
change(a[i-1],a[i],i);
for(pair<int,int> now:Q[i]) ans[now.se]=n-bit.que(now.fi);
}
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}