2023/2/23模拟赛

本来还是会打一百多分的。。。。但是又是因为空间问题挂了五十几分,之后写题时要重视空间这个问题啊。

\(T1\)

image

关于这个题,从中学到的最重要的东西就是分层图的应用。感觉这个可以参考一下这个

个人认为,分层图就是根据点/边的不同性质将其分类,建上好几个一样的图,再在不同的图之间连上相应的边,使得图之间不相互干扰,达到一种分类讨论的效果。

那么放到这个题里,可以根据计时器的三个状态来将点分类。假设走到点 \(i\) 时计时器为 \(k\),那么再走一条边,计时器就会变为 \(k + 1 \mod 3\),建好分层图后,我们只需判断点 \(i,0\) 能否到达 \(i,1\) 即可。用 \(tarjan\) 来找强连通分量即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e6;
int head[MAXN + 5],belong[MAXN + 5],n,m,tot,low[MAXN + 5],dfn[MAXN + 5],tim,s[MAXN + 5],cnt;
bool vis[MAXN + 5];
struct EDGE{
	int u,v,next;
}edge[MAXN + 5];
void add(int u,int v){
	++tot;
	edge[tot].u = u;
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}
void tarjan(int u){
	s[++s[0]] = u;
	vis[u] = 1;
	low[u] = dfn[u] = ++tim;
	for(int i = head[u]; i; i = edge[i].next){
		int v = edge[i].v;
		if(!dfn[v]){
			tarjan(v);
			low[u] = min(low[u],low[v]);
		}
		else if(vis[v])low[u] = min(low[u],dfn[v]);
	}
	if(dfn[u] == low[u]){
		++cnt;
		while(s[s[0] + 1] != u){
			int v = s[s[0]];
			belong[v] = cnt;
			s[0]--;
			vis[v] = 0;
		}
	}
}
int main(){
	freopen("circle.in","r",stdin);
	freopen("circle.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= m; i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(3 * u,3 * v - 1),add(3 * u - 1,3 * v - 2),add(3 * u - 2,3 * v);//走到u时计时器为0与走到v时计时器为1相连,走到u时计时器为1与走到v时计时器为2相连,以此类推	
	}
	for(int i = 1; i <= 3 * n; i++){
		if(!dfn[i])tarjan(i);
	}
	int ans = 0;
	for(int i = 1; i <= n; i++){
		if(belong[i * 3] == belong[i * 3 - 1])++ans;
	}
	cout << ans;
}

\(T2\)

image
期望 \(DP\)……不会……留待以后再思。
image

点击查看代码
#include<algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include  <stdio.h>
#include   <math.h>
#include   <time.h>
#include   <vector>
#include    <queue>
#include      <map>
#include      <set>
using namespace std;

typedef long long LL;

const int N=55,M=9,Mod=258280327;

int g[N][N][M][1<<M],f[N][M],s[N][N][M],C[N][N],n,m,pw2[N*N];

int Pow(int a,int b)
{
	int t=1;
	while(b)
	{
		if(b&1)
			t=t*(LL)a%Mod;
		a=a*(LL)a%Mod;b>>=1;
	}
	return t;
}

int dfs(int a,int b,int m,int d)
{
	if(a>b)
		swap(a,b);
	if(a==0||d<=0)
		return pw2[(a+b)*m];
	if(d>=(1<<m))
		return 0;
	int &sum=g[a][b][m][d];
	if(sum!=-1)
		return sum;
	sum=0;
	for(int i=0;i<=a;i++)
		for(int j=0;j<=b;j++)
			if((i==0&&j==b)||(i==a&&j==0))
				sum=(sum+dfs(a,b,m-1,d-(1<<m-1)))%Mod;
			else
				sum=(sum+(LL)dfs(i,j,m-1,d)*dfs(a-i,b-j,m-1,d)%Mod*C[a][i]%Mod*C[b][j])%Mod;
	return sum;
}

int work(int a,int b,int m)
{
	if(m<=0)
		return 0;
	if(a>b)
		swap(a,b);
	int &sum=s[a][b][m];
	if(sum!=-1)
		return sum;
	sum=0;
	for(int i=1;i<(1<<m);i++)
		sum=(sum+dfs(a,b,m,i))%Mod;
	return sum;
}

int solve(int n,int m)
{
	if(m<=0||n<=1)
		return 0;
	int &sum=f[n][m];
	if(sum!=-1)
		return sum;
	sum=2*solve(n,m-1)%Mod;
	for(int i=1;i<n;i++)
		sum=(sum+(solve(i,m-1)*(LL)pw2[(n-i)*(m-1)]+solve(n-i,m-1)*(LL)pw2[i*(m-1)]+work(i,n-i,m-1)+(1<<m-1)*(LL)pw2[n*(m-1)])%Mod*C[n][i])%Mod;
	return sum;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("young.in","r",stdin);
	freopen("young.out","w",stdout);
#endif
	memset(g,-1,sizeof g);memset(f,-1,sizeof f);memset(s,-1,sizeof s);
	cin>>n>>m;
	pw2[0]=1;
	for(int i=1;i<=n*m;i++)
		pw2[i]=pw2[i-1]*2%Mod;
	for(int i=0;i<=n;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mod;
	}
	cout<<solve(n,m)*(LL)Pow(Pow(1<<m,n),Mod-2)%Mod<<endl;
	return 0;
}

\(T3\)

image
正解是莫比乌斯反演\(+\)杜教筛,杜教筛也不太会,今天试试能不能看懂前 \(60%\) 做法。
image

点击查看代码
#include<algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include  <stdio.h>
#include   <math.h>
#include   <time.h>
#include   <vector>
#include    <queue>
#include      <map>
#include      <set>
using namespace std;

typedef long long LL;

const int N=11000000,Mod=258280327;

LL n;
int p[N/10],tot,m,f[N],Num;
char mu[N],P[N];

void sieve(int n)
{
	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!P[i])
			p[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&i*p[j]<=n;j++)
		{
			int k=i*p[j];
			P[k]=1;
			if(i%p[j]==0)
			{
				mu[k]=0;break;
			}
			mu[k]=-mu[i];
		}
	}
	for(int i=1;i<=n;i++)
		f[i]=(f[i-1]+mu[i]*i)%Mod;
}

map<LL,int> F;

inline int sum2(LL n)
{
	n%=Mod;
	return (n*(n+1)>>1)%Mod;
}

inline int solve(LL x)
{
	if(x<=m)
		return f[x];
	else
		if(F.count(x))
			return F[x];
	int S=1,las=1,now;
	for(LL l=2,r;l<=x;)
	{
		LL k=x/l;
		r=x/k,now=sum2(r),S=(S-(now-las)*(LL)(k<=m?f[k]:F[k]))%Mod,l=r+1,las=now;
	}
	if(S<0)
		S+=Mod;
	return F[x]=S;
}

inline int Pow(LL x,LL y)
{
	x%=Mod;int s=1;
	while(y)
	{
		if(y&1)
			s=s*x%Mod;
		x=x*x%Mod;y>>=1;
	}
	return s;
}

inline int sum(LL n)
{
	static LL Ny=Pow(9,Mod-2);
	return (n%Mod*Pow(10,n+1)%Mod-(Pow(10,n+1)-10)*Ny%Mod)*Ny%Mod;
}

void solve()
{
	m=min(10000000ll,n);
	sieve(m);
	LL Ans=0,las=0,now=0;
	for(LL l=1,r;l<=n;)
		r=n/(n/l),now=solve(r),Ans=(Ans+(now-las)*sum(n/l))%Mod,l=r+1,las=now;
	cout<<(Ans+Mod)%Mod<<endl;
}

int main()
{
	freopen("simple.in","r",stdin);
	freopen("simple.out","w",stdout);
	scanf("%lld",&n);
	solve();
	return 0;
}

\(T4\)

image
这个题可以类比于经典的高楼摔鸡蛋问题,只不过数据范围加强了。

\(30\%\) 做法:

\(dp[i][j]\) 为当有 \(i\) 个鸡蛋,楼层为 \(j\) 时最少多少次才能判断出哪一个楼层掉下来会摔碎。方程为:

\[dp[k][n] = \min(\max{dp[k - 1][n - 1],dp[k][n - j]} + 1) \]

$50$ 做法:

\(k = 1\)\(k = 2\) 进行特判。

\(80\%\) 做法:

换一个思路 \(dp\),设 \(dp[i][j]\)\(j\) 个鸡蛋实验 \(i\) 次最高能探测到的楼层,转移方程为:

\[dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] + 1 \]

加上神奇的优化:\(dp[i][j] = \sum_{x = 1}^j {j \choose x}\),这个由数学归纳法证

时间复杂度 \(O(Tk\log n)\)

\(100\%\) 做法:

发现当 \(k = 1\) 时和 \(k = 2\) 时答案会很大,对这两种情况单独处理,其他情况 \(dp\) 即可,时间复杂度 \(O(T\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e5;
const int INF = 1000000000000000000;
int T,n,k,dp[MAXN + 5][100];
bool check(int x){
	if(x <= MAXN)return dp[x][k] >= n;
	int now = x,s = x;
	for(int i = 2; i <= k; i++){
		if(now * (long double)(x - i + 1) / i >= 1e18)return 1;
		now = now * (x - i + 1) / i;
		s = s + now;
		if(s >= n)return 1;
	}
	return s >= n;
}	
signed main(){
	freopen("naive.in","r",stdin);
	freopen("naive.out","w",stdout);
	for(int i = 1; i <= MAXN; i++){
		for(int j = 1; j <= 64; j++){
			dp[i][j] = min(INF,1 + dp[i - 1][j] + dp[i - 1][j - 1]);
		} 
	}
	scanf("%lld",&T);
	while(T--){
		scanf("%lld%lld",&n,&k);
		int l = 0,r = n - 1;
		r = INF - 1;
		while(l + 1 < r){
			int mid = (l + r) / 2;
			if(check(mid))r = mid;
			else l = mid;
		}
		cout << r << "\n";
	}
}
posted @ 2023-02-24 11:14  腾云今天首飞了吗  阅读(31)  评论(1编辑  收藏  举报