noip多校模拟23

Posted WindZR

tags:

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

考试过程:开考先看题,觉得按顺序开题比较合适。首先是T1,刚开始没什么思路,而且比较困,神游了半个小时。然后突然有了思路,想到了回文串必定会有一个对称中心,那么我们可以从这个中心出发向两边扩展,设当前矩形的左上角和右下角分别是\\(x1,y1,x2,y2\\),那么可以扩展到
\\(x1-1,y1,x2+1,y2\\)
\\(x1-1,y1,x2,y2+1\\)
\\(x1,y1-1,x2+1,y2\\)
\\(x1,y1-1,x2,y2+1\\)
四个方向的矩形,直接转移即可,这样的复杂度是\\(o(n^4)\\),可以获得\\(50pts\\),然后我想了一会,觉得应该可以用类似与步数的方式优化到\\(o(n^3)\\),但是没什么具体的思路,就先打后面的题。
T2,阅读程序题,指针满天飞,属实看不懂,就弃掉了。
T3,想了一会想到一个\\(dp\\)思路,可以拿到\\(44pts\\),但是考场上打挂了,只拿了\\(12pts\\),错了好多小点,所以以后一定要测试点分治。
T4,没什么时间想了,就按照题意模拟,拿了\\(42pts\\)
总结:1.一定要打测试点分治
2.在看不懂题的情况下不要放弃,可以先看特殊性质,拿到白送的分。
3.如果有优化的思路,就要留出时间思考。

T1 回文

思路:暴力思路上面已经说过了,现在考虑如何优化。从对称中心出发,那么到达\\((1,1)\\)\\((n,m)\\)的步数是一样的,所以一定满足这样的柿子\\(x1=n-x2+1,y1=m-y2+1\\),那么一定满足\\(x1+y1+x2+y2==n+m+2\\),那么如果我们知道了\\(x1,y1,x2\\),那么\\(y2\\),可以通过计算得知,这样的复杂度就降到了\\(o(n^3)\\)
所以我们设\\(f_{i,j,k}\\)表示枚举的矩形的左上角为\\((i,j)\\),右下角的横坐标为\\(k\\)的方案数。转移是显然的。
最后考虑如何统计答案,我们找到所有\\(x+y-1==((n+m-2)/2)+1\\)的点,那么只需要\\(ans+=f_{x,y,x+1}+f_{x,y,x}\\)即可。
代码如下:

AC_code

#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int mo=993244853;
const int N=505;
int f[N][N][N];
char s[510][510];
int n,m,ans;
ii read()
{
	int x=0; char ch=getchar(); bool f=1;
	while(ch<\'0\' or ch>\'9\')
	{
		if(ch==\'-\') f=0;
		ch=getchar();
	}
	while(ch>=\'0\' and ch<=\'9\')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
iv add(int &x,int y)
{
	x=x+y;
	if(x>=mo) x-=mo;
}
int main()
{
	freopen("palin.in","r",stdin);
	freopen("palin.out","w",stdout);
	n=read(),m=read();
	for(re i=1;i<=n;i++)
	{
		scanf("%s",s[i]+1);
	}
	if(s[1][1]!=s[n][m]) {printf("0\\n");return 0;}
	f[1][1][n]=1;
	for(re i=1;i<=n;i++)
	{
		for(re j=1;j<=m;j++)
		{
			for(re k=n;k>=i;k--)
			{
				int p=n+m-i-j-k+2;
				if(i==k and j==p) continue;
				if(p<j) continue;
				if((k-1>=i and j+1<=p) and s[i][j+1]==s[k-1][p]) add(f[i][j+1][k-1],f[i][j][k]);
				if((p-1>=j+1) and s[i][j+1]==s[k][p-1]) add(f[i][j+1][k],f[i][j][k]);
				if((k-1>=i+1) and s[i+1][j]==s[k-1][p]) add(f[i+1][j][k-1],f[i][j][k]);
				if((i+1<=k and p-1>=j)  and s[i+1][j]==s[k][p-1]) add(f[i+1][j][k],f[i][j][k]);
			}
		}
	}
	int len=(n+m-2)>>1;
	for(re i=1;i<=n;i++)
	{
		for(re j=1;j<=m;j++)
		{
			if((i+j-1)==len+1)
			{
				add(ans,f[i][j][i+1]);
				add(ans,f[i][j][i]);
			}
		}
	}
	printf("%d\\n",ans);
	return 0;
}


T2 快速排序

思路:经过计算机带师ICEY的讲解,终于看懂了题意。
首先,如果所有被排序数都非 nan,那么必然是按升序排列。
否则,我们考虑第一个数 \\(x\\)
如果 \\(x\\)\\(nan\\),那相当于把一个 \\(nan\\) 置于最前,然后排序其余的数。
否则,相当于把所有剩下的 \\(< x\\) 的数从小到大放在 \\(x\\) 之前,并放置 \\(x\\)
那么这个过程可以使用优先队列来实现。代码如下:

AC_code

#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=5e5+10;
struct node
{
	bool is;
	int val;
}cun[N];
int t,n,cnt;
priority_queue<int,vector<int>,greater<int> >Q;
ii read()
{
	int x=0; char ch=getchar(); bool f=1;
	while(ch<\'0\' or ch>\'9\')
	{
		if(ch==\'n\')
		{
			f=0;
			ch=getchar();
			ch=getchar();
			ch=getchar();
			return -1;
		}
		ch=getchar();
	}
	while(ch>=\'0\' and ch<=\'9\')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-1);
}
int main()
{
	freopen("qsort.in","r",stdin);
	freopen("qsort.out","w",stdout);
	t=read();
	while(t--)
	{
		n=read();
		for(re i=1;i<=n+1;i++) cun[i]=(node){0,0};
		for(re i=1;i<=n;i++)
		{
			int x=read();
			if(x==-1) cun[i].is=1;
			else {cun[i].val=x,Q.push(x);}
		}
		for(re i=1;i<=n;++i)
		{
			if(cun[i].is==1) printf("nan ");
			else
			{
				while(Q.size() and Q.top()<cun[i].val) printf("%d ",Q.top()),Q.pop();
				if(Q.size() and Q.top()==cun[i].val) printf("%d ",Q.top()),Q.pop();
			}
		}
		printf("\\n");
	}
	return 0;
}


T3 混乱邪恶

思路:目前只会打部分分,说一下部分分的思路:我们设\\(f_{i,j0/1}\\)表示前\\(i\\)个数能否凑出和为\\(j\\),如果可以,那么最后一个数的位置是什么。那么我们就可以暴力转移了。考虑优化,我们显然可以滚掉一维,但是要注意,如果之前已经凑出过当前位置的数了,那么就不需要更新了,否则因为我利用的是之前的数进行的更新,当我修改之后,当我统计答案的时候就不对了。
代码如下:

AC_code

#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=1e6+10;
int n,m,sum;
int a[N],ans[N];
int f[N][2],pre[N][2];
bool flag;
ii read()
{
	int x=0; char ch=getchar(); bool f=1;
	while(ch<\'0\' or ch>\'9\')
	{
		if(ch==\'-\') f=0;
		ch=getchar();
	}
	while(ch>=\'0\' and ch<=\'9\')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
signed main()
{
	freopen("chaoticevil.in","r",stdin);
	freopen("chaoticevil.out","w",stdout);
	n=read(),m=read();
	for(re i=1;i<=n;i++) a[i]=read(),sum+=a[i];
	sum/=2;
	for(re i=1;i<=n;i++) ans[i]=-1;
	f[a[1]][0]=f[a[1]][1]=1;
	for(re i=2;i<=n;i++)
	{
		for(re j=sum-a[i];j;j--)
		{
			if( (!f[j+a[i]][0]) and (f[j][0]) )
			{
				f[j+a[i]][0]=1;
				f[j+a[i]][1]=i;
				pre[j+a[i]][0]=f[j][1];	
				pre[j+a[i]][1]=j;
			}
		}
		if(!f[a[i]][0])
		{
			f[a[i]][0]=1,f[a[i]][1]=i;
			pre[a[i]][0]=pre[a[i]][1]=0;
		}
		if(f[sum][0]) {flag=1;break;}
	}
	if(flag)
	{
		printf("NP-Hard solved\\n");
		ans[f[sum][1]]=1;
		while(sum and pre[sum][0])
		{
			ans[pre[sum][0]]=1;
			sum=pre[sum][1];
		}
		for(re i=1;i<=n;i++)
			printf("%d ",ans[i]);
		printf("\\n");
	}
	else printf("Chaotic evil\\n");
	return 0;
}


T4 校门外歪脖树上的鸽子

咕了。

NOIP模拟86(多校19)

T1 特殊字符串

解题思路

\\(f_{i,j}\\) 表示前 \\(i\\) 个字符中结尾为 \\(j\\) 的最大贡献。

转移枚举当前位置于之前位置结尾的组合加上贡献即可。

对于边界问题,容易发现选择 1 一定不劣。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<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,INF=1e18;
int n,m,ans,f[N][30],val[30][30];
char s[N],ch1,ch2;
#undef int
int main()
{
	#define int long long
	freopen("shiki.in","r",stdin); freopen("shiki.out","w",stdout);
	n=read(); scanf("%s",s+1); m=read();
	for(int i=1,v;i<=m;i++)
	{
		getchar(); ch1=getchar(); getchar(); ch2=getchar(); v=read();
		val[ch1-\'a\'][ch2-\'a\']+=v;
	}
	for(int i=1;i<=n;i++) for(int j=0;j<26;j++) f[i][j]=-INF; f[1][s[1]-\'a\']=0;
	for(int i=2;i<=n;i++)
	{
		for(int j=0;j<26;j++) f[i][j]=f[i-1][j];
		for(int j=0;j<26;j++) f[i][s[i]-\'a\']=max(f[i][s[i]-\'a\'],f[i-1][j]+val[j][s[i]-\'a\']);
	}
	for(int i=0;i<26;i++) ans=max(ans,f[n][i]); printf("%lld",ans);
	return 0;
}

T2 宝可梦

解题思路

调了一下午,换了两三种做法,最后换回我原来的做法,尝试着调了一下段错误,发现真的只是数组开小了那么简单!!!

我还以为是系统栈空间炸了。。。

如果当前方向是可以走的话直接向前走,否则的话因为是沿着右墙走所以直接左转。

然后对于右手边没有墙的时候,就直接向右手边走就好了。50行精品超短小暴力

由于题目保证路径是唯一的因此所有点之间形成了一种树形结构。

那么我们选择一个出发点一直走一定会遍历完整张图,并且形成了一个欧拉序。

于是我们就可以对于一个点按照正反方向走两边(询问是有方向的),对于每一个询问查找对应点之间的距离就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<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=5e5+10,INF=1e18;
int n,m,q,all,sum[10],pos[5][N][5];
int r[10]={0,4,3,1,2},l[10]={0,3,4,2,1};
int d1[10]={0,-1,1,0,0},d2[10]={0,0,0,-1,1};
vector<char> e[N];
bool vis[N];
char ch[N];
inline int id(int x,int y){return (x-1)*m+y;}
void work(int x,int y,int fx)
{
	int p=fx,tx=x,ty=y;
	pos[p][id(x,y)][fx]=min(pos[p][id(x,y)][fx],sum[p]);
	int nx=x+d1[fx],ny=y+d2[fx];
	if(e[nx][ny]==\'.\') sum[p]++,x=nx,y=ny;
	else fx=l[fx];
	if(e[x+d1[r[fx]]][y+d2[r[fx]]]==\'.\') fx=r[fx];
	while(x!=tx||y!=ty||p!=fx)
	{
		pos[p][id(x,y)][fx]=min(pos[p][id(x,y)][fx],sum[p]);
		int nx=x+d1[fx],ny=y+d2[fx];
		if(x==tx&&y==ty&&fx==p) break;
		if(e[nx][ny]==\'.\') sum[p]++,x=nx,y=ny;
		else for(int i=1;i<=3;i++)
		{
			fx=r[fx];
			if(x==tx&&y==ty&&fx==p) break;
		}
		if(x==tx&&y==ty&&fx==p) break;
		if(e[x+d1[r[fx]]][y+d2[r[fx]]]==\'.\') fx=r[fx];
	}
}
#undef int
int main()
{
	#define int long long
	freopen("pokemon.in","r",stdin); freopen("pokemon.out","w",stdout);
	n=read(); m=read(); memset(pos,0x3f,sizeof(pos));
	for(int i=1;i<=n;i++)
	{
		e[i].push_back(\'X\'); scanf("%s",ch+1);
		for(int j=1;j<=m;j++) e[i].push_back(ch[j]);
		e[i].push_back(\'X\');
	}
	for(int i=0;i<=m+1;i++) e[0].push_back(\'X\'),e[n+1].push_back(\'X\');
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) all+=(e[i][j]==\'.\');
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(e[i][j]==\'.\'){for(int k=1;k<=2;k++)work(i,j,k);goto V;}
	V:; q=read();
	while(q--)
	{
		int x,y,tx,ty,fx; x=read(); y=read(); tx=read(); ty=read();
		scanf("%s",ch+1); fx=(ch[1]==\'U\')?1:((ch[1]==\'D\')?2:((ch[1]==\'L\')?3:4));
		int ans=INF;
		for(int p=1;p<=2;p++)
			for(int i=1;i<=4;i++)
			{
				if(pos[p][id(x,y)][fx]>=INF||pos[p][id(tx,ty)][i]>=INF) continue;
				if(pos[p][id(tx,ty)][i]>=pos[p][id(x,y)][fx]) ans=min(ans,pos[p][id(tx,ty)][i]-pos[p][id(x,y)][fx]);
				else ans=min(ans,sum[p]+(pos[p][id(tx,ty)][i]-pos[p][id(x,y)][fx]));
			}
		printf("%lld\\n",ans);
	}
	return 0;
}

T3 矩阵

解题思路

其实就是一个爆搜,显然如果有两个相邻的点数值相等答案就是 -1 。

否则的话由于是等比数列搜索深度不会超过 \\(log\\) 层,因此复杂度是对的。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define left Left
#define right Right
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=4e4+10;
int n,m,ans=1;
int d1[10]={0,1,-1,0,0};
int d2[10]={0,0,0,1,-1};
unordered_map<int,int> mp[N];  
vector<int> s[N];
inline int id(int x,int y){return (x-1)*m+y;}
inline bool check(){return (1.0*clock())/(1.0*CLOCKS_PER_SEC)<=0.98;}
void dfs(int x,int y,int dis,int num)
{
	ans=max(ans,dis); 
	for(int i=1;i<=4;i++)
	{
		int nx=x+d1[i],ny=y+d2[i];
		if(s[nx][ny]==s[x][y]*num)
			dfs(nx,ny,dis+1,num);
	}
}
void solve(int i,int j)
{
	for(int k=1;k<=4;k++)
	{
		int x=i+d1[k],y=j+d2[k];
		if(s[x][y]<s[i][j]||s[x][y]%s[i][j]) continue;
		dfs(x,y,2,s[x][y]/s[i][j]);
	}
}
#undef int
int main()
{
	#define int long long
	freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout);
	n=read(); m=read(); for(int i=0;i<=m+1;i++) s[0].push_back(0),s[n+1].push_back(0);
	for(int i=1;i<=n;i++)
	{
		s[i].push_back(0);
		for(int j=1;j<=m;j++) s[i].push_back(read());
		s[i].push_back(0);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=4;k++)
			{
				int x=i+d1[k],y=j+d2[k];
				if(s[x][y]==s[i][j]) printf("-1"),exit(0);
			}
	for(int i=1;i<=n&&check();i++)
		for(int j=1;j<=m&&check();j++)
			solve(i,j);
	printf("%lld",ans);
	return 0;
}

T4 乘法

大坑未补

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

noip多校模拟25

noip模拟70

noip多校21

5.23考试总结(NOIP模拟2)

10.23 noip模拟试题

9.23 NOIP模拟赛总结