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\)

每次统计方案数就好啦。

luobotianle 的题解

int_R 的题解

APJ 的题解

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;
}
posted @ 2024-08-12 18:00  ppllxx_9G  阅读(25)  评论(2编辑  收藏  举报