NOIP模拟73

Posted Varuxn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP模拟73相关的知识,希望对你有一定的参考价值。

T1 小L的疑惑

解题思路

第一眼不是正解,又是 bitset 优化可以得到的 60pts 的部分分。

打着打着突然发现这个东西好像和之前做过的某个题有一些相似,试着打了一下。

然后样例过了,然后对拍没错,然后就切了??

先排序,如果某个数之前所有数的和都比这个数字-1 小,那么这里就是一个空缺,扫的时候选择最小的空缺就是答案。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
	while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e5+10,M=1e7+10,INF=1e18;
int n,cnt,s[N];
signed main()
{
	freopen("math.in","r",stdin); freopen("math.out","w",stdout);
	n=read(); for(int i=1;i<=n;i++) s[i]=read(); sort(s+1,s+n+1);
	for(int i=1;i<=n;i++)
	{
		if(cnt<s[i]-1) printf("%lld",cnt+1),exit(0);
		cnt+=s[i];
	}
	printf("%lld",cnt+1);
	return 0;
}

T2 小L的数列

解题思路

发现运算都是乘法,于是我们考虑转化成为指数上的加法。

然后我们又看了一下 k 的范围,大概是矩阵乘法没错了,于是很好出来下移的做法复杂度大概是 \\(k^3log(n-k)\\)

但是我考场上一时糊涂竟然没有想到,于是整到了一个 \\(k^3log\\frac{n-k}{k}\\) 的做法,也就是一次性转移 k 个。

单位矩阵的第一行就是 \\(b_k,b_{k-1},...b_2,b_1\\) 第二行其实就是 \\(0,b_{k},...b_3,b_2\\) 再加上第一行的系数每一项乘上一个 \\(b_1\\) 其它的系数也是类似。

然后我们就可以一次性转移 k 个了,免去了卡常的麻烦。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
	while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=4e7+10,M=210,mod=998244353;
int n,m,Ans=1,f[M],b[M];
struct Square
{
	int a[M][M];
	void clear(){memset(a,0,sizeof(a));}
	Square friend operator * (Square x,Square y)
	{
		Square z; z.clear();
		for(int i=1;i<=m;i++)
			for(int j=1;j<=m;j++)
				for(int k=1;k<=m;k++)
					z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j])%(mod-1);
		return z;
	}
}e,ans;
void pw(Square &x,int y)
{
	while(y)
	{
		if(y&1) x=x*e;
		e=e*e; y>>=1;
	}
}
int power(int x,int y,int p=mod)
{
	int temp=1;
	while(y)
	{
		if(y&1) temp=temp*x%mod;
		x=x*x%mod; y>>=1;
	}
	return temp;
}
signed main()
{
	freopen("seq.in","r",stdin); freopen("seq.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=m;i++) b[i]=read();
	for(int i=1;i<=m;i++) f[i]=read();
	for(int i=1;i<=m;i++) ans.a[i][i]=1;
	for(int i=1;i<=m;i++)
	{
		for(int j=i;j<=m;j++) e.a[i][j]=b[m-(j-i)];
		for(int j=1;j<i;j++)
			for(int k=1;k<=m;k++)
				e.a[i][k]=(e.a[i][k]+e.a[j][k]*b[i-j])%(mod-1);
	}
	pw(ans,n/m+(n%m!=0)-1);
	int pos=n%m; if(!pos) pos=m;
	for(int i=1;i<=m;i++)
		Ans=Ans*power(f[i],ans.a[pos][i])%mod;
	printf("%lld",Ans);
	return 0;
}

T3 连边

解题思路

尽管实际得分远高于期望得分,但是还是挂了 20pts 没有场上切掉(感觉不稳就判了一下暴力,然后暴力被卡常了。。)

大概就是一个多源最短路同时记录一下前驱,对于只有一个前驱的直接选择,对于多个前驱的选择权值最小的。。

本来我只是想对于随机数据下手的,想随便 rand() 一下,但是感觉不稳,就贪了个心,然后歪打正着??(算是吧。。)

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
	while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=2e5+10,M=N<<1,INF=1e18;
int n,m,ans=INF,dis[N],fa[N];
int tot=1,head[N],nxt[M<<1],ver[M<<1],edge[M<<1];
bool col[N],vis[N],can[M];
struct Road{int l,r,val;}pat[M];
priority_queue<pair<int,int> > q;
vector<pair<int,int> > pre[N];
void add_edge(int x,int y,int val)
{
	ver[++tot]=y; edge[tot]=val;
	nxt[tot]=head[x]; head[x]=tot;
}
int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
signed main()
{
	freopen("minimum.in","r",stdin); freopen("minimum.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;i++) vis[i]=col[i]=read(),fa[i]=i;
	for(int i=1,x,y,val;i<=m;i++)
	{
		x=read(); y=read(); val=read(); pat[i]=(Road){x,y,val},
		add_edge(x,y,val); add_edge(y,x,val);
		if(find(x)==find(y)) continue;
		vis[find(y)]|=vis[find(x)];
		fa[find(x)]=find(y);
	}
	for(int i=1;i<=n;i++) if(!vis[find(i)]) printf("impossible"),exit(0);
	memset(dis,0x3f,sizeof(dis)); memset(vis,false,sizeof(vis));
	for(int i=1;i<=n;i++) if(col[i]) dis[i]=0,q.push(make_pair(0,i));
	while(!q.empty())
	{
		int x=q.top().second; q.pop();
		if(vis[x]) continue; vis[x]=true;
		for(int i=head[x];i;i=nxt[i])
		{
			int to=ver[i],val=edge[i];
			if(dis[to]<dis[x]+val) continue;
			if(dis[to]>dis[x]+val) vector<pair<int,int> >().swap(pre[to]);
			dis[to]=dis[x]+val; pre[to].push_back(make_pair(x,i));
			if(!vis[to]) q.push(make_pair(-dis[to],to));
		}
	}
	for(int i=1;i<=n;i++) if(pre[i].size()==1) can[pre[i][0].second>>1]=true;
	for(int i=1;i<=n;i++)
		if(pre[i].size()!=1)
		{
			int minn=INF,id=0;
			for(int j=0;j<pre[i].size();j++) if(can[pre[i][j].second>>1]) goto X;
			for(int j=0;j<pre[i].size();j++) if(minn>edge[pre[i][j].second]) id=pre[i][j].second,minn=edge[id],id>>=1;
			can[id]=true; X:;
		}
	ans=0;
	for(int i=1;i<=m;i++) ans+=can[i]*pat[i].val;
	printf("%lld",ans);
	return 0;
}

T4 小L的有向图

解题思路

看到 T4 的时候只有 30min 了,感觉不是特别好搞,我直接 printf("0") 然后去看前面的了。。

考完之后听了一下正解思路感觉神似竞赛图,但又不完全是。

\\(f_{S}\\) 表示 \\(S\\) 点集内部合法拓扑序的个数然后枚举集合外面的点,计算集合内点到对应点的连边数量作为 2 的指数就是这个点的贡献。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
	while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=25,mod=998244353;
int n,m,e[N],f[1<<22],sum[1<<22];
int lowbit(int x){return x&(-x);}
signed main()
{
	freopen("topology.in","r",stdin); freopen("topology.out","w",stdout);
	n=read(); m=read();
	for(int i=1,x,y;i<=m;i++) x=read(),y=read(),e[y]|=1<<x-1;
	for(int i=1;i<=n;i++) sum[1<<i-1]=1; f[0]=1;
	for(int i=1;i<(1<<n);i++)sum[i]=sum[i^lowbit(i)]+sum[lowbit(i)];
	for(int sta=0;sta<(1<<n);sta++)
		for(int i=1;i<=n;i++)
		{
			if((sta>>i-1)&1) continue;
			int temp=sum[sta&e[i]];
			f[sta|(1<<i-1)]=(f[sta|(1<<i-1)]+f[sta]*(1ll<<temp))%mod;
		}
	printf("%lld",f[(1<<n)-1]);
	return 0;
}

T? 中国象棋

解题思路

由于今天的考试题目比较简单,教练就又加了几道题,看了看好像就这道比较好做,大致口胡一下。

显然每行每列最多只可能有两个棋子,于是设 \\(f_{i,j,k}\\) 表示前 i 行,有 j 列只有一个棋子,有 k 列只有两个棋子。

然后分别枚举不放旗子,在有一个棋子的列放一个,在没有的放一个或者两个,在没有的有一个的各放一个,在有一个的放两列。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>\'9\'||ch<\'0\'){if(ch==\'-\')f=-1;ch=getchar();}
	while(ch>=\'0\'&&ch<=\'9\'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=110,mod=9999973;
int n,m,ans,f[N][N][N];
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int C2(int x){return x*(x-1)/2;}
void solve(int i,int j,int k)
{
	if(!f[i][j][k]) return ;
	add(f[i+1][j][k],f[i][j][k]);
	add(f[i+1][j+1][k],f[i][j][k]*(m-j-k)%mod);
	add(f[i+1][j+2][k],f[i][j][k]*C2(m-j-k)%mod);
	if(j>=2) add(f[i+1][j-2][k+2],f[i][j][k]*C2(j)%mod);
	if(j>=1) add(f[i+1][j-1][k+1],f[i][j][k]*j%mod),add(f[i+1][j][k+1],f[i][j][k]*(m-j-k)%mod*j%mod);
}
signed main()
{
	freopen("chess.in","r",stdin); freopen("chess.out","w",stdout);
	n=read(); m=read(); f[0][0][0]=1;
	for(int i=0;i<n;i++) for(int j=0;j<=m;j++) for(int k=0;k<=m-j;k++) solve(i,j,k);
	for(int i=0;i<=m;i++) for(int j=0;j<=m-i;j++) add(ans,f[n][i][j]);
	printf("%lld",ans); return 0;
}

2021.11.18-NOIP模拟测试信心赛

2021.11.18-NOIP模拟信心赛

前言

太蒟蒻了,信心赛打的都快没信心了,giao

T1\\(\\color{green}100\\)题目

T1作为简单的签到题,直接先枚举所有的流量再用dijkstra跑这么多遍就可以了

#include<bits/stdc++.h>
#define M 2100
#define N 1100
#define inf 0x7f7f7f7f
using namespace std;
int n,m,maxx=-15;
int first[M],nex[M],to[M],w[M],f[M],tot;
int dis[N];
bool vis[N];
priority_queue<pair<int,int> >q;
//=======================================================
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(!isdigit(c)){if(c==\'-\')f=-1;c=getchar();}
	while(isdigit(c)){p=p*10+c-\'0\';c=getchar();}
	return p*f;
}
//========================================================
inline void add(int x,int y,int z,int q){
	nex[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
	f[tot]=q;
}
//========================================================
inline void dijkstra(){
	for(int x=1;x<=1000;x++){
		for(int i=1;i<=n;i++){
			dis[i]=inf;
			vis[i]=0;
		}
		q.push(make_pair(0,1));
		dis[1]=0;	
		while(!q.empty()){
			int u=q.top().second;
			q.pop();
			if(vis[u])continue;
			vis[u]=1;
			for(int i=first[u];i;i=nex[i]){
				int v=to[i];
				if(f[i]<x)continue;
				if(dis[u]+w[i]<dis[v]){
					dis[v]=dis[u]+w[i];
					if(!vis[v]){
						q.push(make_pair(-dis[v],v));
					}
				}
			}
		}
		if(dis[n]!=inf)
			maxx=max(maxx,x*1000000/dis[n]);
	}
}
//========================================================
int main(){
	n=read(),m=read();
	int a,b,c,f;
	for(int i=1;i<=m;i++){
		a=read(),b=read(),c=read(),f=read();
		add(a,b,c,f);
		add(b,a,c,f);
	}
	dijkstra();
	cout<<maxx<<endl;
	return 0;
}

T2\\(\\color{red}0\\)题目

明明第二天比第一题还简单简单我却做不出来

一开始在哪里想建图然后\\(bfs\\)然后小样例过了,大样例却挂了

结果直接用一个并查集分成两个集合就好了(有两种牛奶)

#include<bits/stdc++.h>
using namespace std;
#define N 100010
int n,m;
char s[N];
int f[N];
int ans[N];
//=====================================================
inline int read(){
	int p=0,f=1;
	char c;
	while(!isdigit(c)){if(c==\'-\')f=-1;c=getchar();}
	while(isdigit(c)){p=p*10+c-\'0\';c=getchar();}
	return p*f;
}
//======================================================
int gf(int x){
	if(x == f[x]) return x;
	return f[x] = gf(f[x]);
}
//======================================================
void hb(int x, int y){
	f[gf(x)] = gf(y);
}
//======================================================
int main(){
	n=read(),m=read();
	scanf("%s",s+1);
	int x,y;
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<n;i++){
		x=read(),y=read();
		if(s[x]==s[y])
			hb(x,y);
	}
	for(int i=1;i<=m;i++){
		char c;
		x=read(),y=read(),cin>>c;
		if(gf(x)==gf(y)&&s[x]!=c)ans[i]=0;
		else ans[i]=1;
	}
	for(int i=1;i<=m;i++)cout<<ans[i];
	return 0;
}

T3\\(\\color{red}0\\)题目

第二题都没做出来自然第三题也做不出来

题意:

求一颗树\\(u\\)\\(v\\)的路径上有无某颜色的点

先跑一遍dfs,再求出\\(u\\)\\(v\\)的最近公共祖先,这样就可以得到\\(u\\)\\(v\\)的路径,然后用结构体和vector存储查询的颜色,最近公共祖先以及该点的编号,

最后再来一遍dfs,这一遍相当于把颜色按照节点的深度进行赋值,然后枚举点因为刚才记录了该点所要查询的颜色以及最近公共祖先,而且\\(u\\)\\(v\\)都记录了,所以如果该点颜色深度大于最近公共祖先的深度,就说明一定能够经过,就把\\(ans[now.id]=1\\)

第三题代码复杂度\\(O\\)\\(n\\log n\\)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 100,M = 2e5 + 100;
const int maxdep=24;
int n,m;
int first[M],to[M],nex[M],tot;
int val[N],dep[N],faz[N][maxdep+1],ans[N];
struct mpair{
	int col,top,id;
};
vector<mpair> vec[N];
//==============================================================
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(!isdigit(c)){if(c==\'-\')f=-1;c=getchar();}
	while(isdigit(c)){p=p*10+c-\'0\';c=getchar();}
	return p*f;
}
//==============================================================
inline void add(int x,int y){
	nex[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}
//==============================================================
void dfs1(int u){
	for(int i=1;i<=maxdep;i++)faz[u][i]=faz[faz[u][i-1]][i-1];
	for(int i=first[u];i;i=nex[i]){
		int v=to[i];
		if(v==faz[u][0])continue;
		dep[v]=dep[u]+1;
		faz[v][0]=u;
		dfs1(v);
	}
}
//==============================================================
inline int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=maxdep;i>=0;i--)
		if(dep[faz[x][i]]>=dep[y])x=faz[x][i];
	if(x==y)return x;
	for(int i=maxdep;i>=0;i--)
		if(faz[x][i]!=faz[y][i])x=faz[x][i],y=faz[y][i];
	return faz[x][0];
}
//==============================================================
int tmp[N];
void dfs(int u){
	int lst=tmp[val[u]];
	tmp[val[u]]=dep[u];
	for(int i=0;i<vec[u].size();i++){
		mpair now=vec[u][i];
		if(tmp[now.col]>=dep[now.top])
			ans[now.id]=1;
	}
	for(int i=first[u];i;i=nex[i]){
		int v=to[i];
		if(v==faz[u][0])continue;
		dfs(v);
	}
	tmp[val[u]]=lst;
}
//==============================================================
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)val[i]=read();
	int x,y;
	for(int i=1;i<n;i++){
		x=read(),y=read();
		add(x,y);
		add(y,x);
	}
	dfs1(1);
	int c;
	for(int i=1;i<=m;i++){
		x=read(),y=read(),c=read();
		int l=lca(x,y);
		vec[x].push_back(mpair{c,l,i});
		vec[y].push_back(mpair{c,l,i});
	}
	memset(tmp,-1,sizeof(tmp));
	dfs(1);
	for(int i=1;i<=m;i++)cout<<ans[i];
	return 0;
}

今天就是集训的最后一天了,明天就是NOIP2021了,祝所有的OIer rp++

以上是关于NOIP模拟73的主要内容,如果未能解决你的问题,请参考以下文章

NOIP模拟赛16

NOIp模拟赛binary

XJOI NOIP模拟题1

神奇的幻方(NOIP2015)(真·纯模拟)

NOIp模拟赛value

NOIP模拟2017.6.11解题报告