20211102NOIP模拟赛

阿巴巴巴,今天又考了一次模拟赛。
不得不说,自己学校出的题就是香,做的最爽的一次.
但是一点也不妨碍我挂掉了60分(局部变量手残不定义为0)。
无语,要不然就是195分了。哎~~~
第一题:
大水题,难度大概是是普及组吧!没错我还是挂掉了,而且还做了将近两个多小时。
思路:
对于每一个操作都设一个函数。比较难的就是九宫格的访问。于是遍历每一个点,然后预处理出它所在的九宫格,放进一个二维数组维护一下。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
char Map[30][30];
int temp[110][30][30];
int Q;
void print(int cnt){
	for(int i=1;i<=9;i++){
		printf("+-+-+-+-+-+-+-+-+-+\n");
		for(int j=1;j<=9;j++){
			printf("|%d",temp[cnt][i][j]);
		}
		printf("|");
		printf("\n");
	}
	printf("+-+-+-+-+-+-+-+-+-+\n");
	return ;
}
int hang[101],lie[101],square[101][101];
int from[101][101],sit[101][101];
int check(int x,int y,int val,int cnt){
	for(int i=1;i<=9;i++){
		if(temp[cnt][x][i]==val){
			return 2;
		}
	}
	for(int i=1;i<=9;i++){
		if(temp[cnt][i][y]==val){
			return 3;
		}
	}
	int a=from[x][y]/10;
	int b=from[x][y]%10;
	for(int i=(a-1)*3+1;i<=a*3;i++){
		for(int j=(b-1)*3+1;j<=b*3;j++){
			if(temp[cnt][i][j]==val){
				return 4;
			}
		}
	}
	return 0;
}
void Insert(int x,int y,int val,int cnt){
	if(temp[cnt][x][y]){
		printf("Error!\n");
		return ;
	}
	int rs=check(x,y,val,cnt);
	if(rs==2){
		printf("Error:row!\n");
		return ;
	}else if(rs==3){
		printf("Error:column!\n");
		return ;
	}else if(rs==4){
		printf("Error:square!\n");
	}else if(rs==0){
		printf("OK!\n");
		temp[cnt][x][y]=val;
	}
}
void Delete(int x,int y,int cnt)
{
	if(temp[cnt][x][y]==0){
		printf("Error!\n");
		return ;
	}else{
		temp[cnt][x][y]=0;
		printf("OK!\n");
	}
	return ;
}

void Query(int x,int y,int cnt){
	if(temp[cnt][x][y]!=0){
		printf("Error!\n");
		return ;
	}
	int tot=0;//这里我挂了60分
	int ans[101];
	for(int i=1;i<=9;i++){
		if(check(x,y,i,cnt)==0){
			ans[++tot]=i;
		}
	}
	printf("%d\n",tot);
	for(int i=1;i<=tot;i++){
		printf("%d\n",ans[i]);
	}
	return ;
}

void Merge(int a,int b,int cnt){
	int tot1=0,tot2=0;
	for(int i=1;i<=9;i++){
		for(int j=1;j<=9;j++){
			if(temp[a][i][j]!=0){
				if(check(i,j,temp[a][i][j],cnt)==0){
					temp[cnt][i][j]=temp[a][i][j];
					tot1++;
				}
			}else if(temp[b][i][j]!=0){
				if(check(i,j,temp[b][i][j],cnt)==0){
					temp[cnt][i][j]=temp[b][i][j];
					tot2++;
				}
			}
		}
	}
	printf("%d %d\n",tot1,tot2);
	return ;
}
int main()
{
	freopen("sudoku.in","r",stdin);
	freopen("sudoku.out","w",stdout);
	for(int i=1;i<=19;i++){
		cin>>(Map[i]+1);
	}
	for(int i=1;i<=19;i++){
		for(int j=1;j<=19;j++){
			if(i%2==0&&j%2==0){
				temp[0][(i)/2][(j)/2]=Map[i][j]-'0';
			}
		}
	}
//下面都不用看了,之前写的状态压缩懒得删,唯一有用的就是from
	for(int i=1;i<=9;i++){
		for(int j=1;j<=9;j++){
			hang[i]=hang[i]*10+temp[0][i][j];
			lie[j]=lie[j]*10+temp[0][i][j];
		}
	}
	for(int i=1;i<=3;i++){
		for(int j=1;j<=9;j++){
			if(j<=3){
				square[1][1]*=10;
				square[1][1]+=temp[0][i][j];
				from[i][j]=11;
			}else if(j>3&&j<=6){
				square[1][2]*=10;
				square[1][2]+=temp[0][i][j];
				from[i][j]=12;
			}else{
				if(j>6){
					square[1][3]*=10;
					square[1][3]+=temp[0][i][j];
					from[i][j]=13;
				}
			}
		}
	}
	for(int i=4;i<=6;i++){
		for(int j=1;j<=9;j++){
			if(j<=3){
				square[2][1]*=10;
				square[2][1]+=temp[0][i][j];
				from[i][j]=21;
			}else if(j>3&&j<=6){
				square[2][2]*=10;
				square[2][2]+=temp[0][i][j];
				from[i][j]=22;
			}else{
				if(j>6){
					square[2][3]*=10;
					square[2][3]+=temp[0][i][j];
					from[i][j]=23;
				}
			}
		}
	}
	for(int i=7;i<=9;i++){
		for(int j=1;j<=9;j++){
			if(j<=3){
				square[3][1]*=10;
				square[3][1]+=temp[0][i][j];
				from[i][j]=31;
			}else if(j>3&&j<=6){
				square[3][2]*=10;
				square[3][2]+=temp[0][i][j];
				from[i][j]=32;
			}else{
				if(j>6){
					square[3][3]*=10;
					square[3][3]+=temp[0][i][j];
					from[i][j]=33;
				}
			}
		}
	}
	scanf("%d",&Q);
	for(int i=1;i<=Q;i++){
		for(int k=1;k<=9;k++){
			for(int j=1;j<=9;j++){
				temp[i][k][j]=temp[i-1][k][j];
			}
		}
		string op;
		int a,b,c;
		cin>>op;
		if(op[0]=='I'){
			scanf("%d %d %d",&a,&b,&c);
			Insert(a,b,c,i);
		}else if(op[0]=='D'){
			scanf("%d %d",&a,&b);
			Delete(a,b,i);
		}else if(op[0]=='Q'){
			scanf("%d %d",&a,&b);
			Query(a,b,i);
		}else if(op[0]=='M'){
			scanf("%d %d",&a,&b);
			for(int k=1;k<=9;k++){
				for(int j=1;j<=9;j++){
					temp[i][k][j]=0;
				}
			}
			Merge(a,b,i);
		}else if(op[0]=='P'){
			print(i);
		}
	}
	return 0;
}
第二题:

算法一:

暴力,使用一个线段树或者优先队列委会区间最大值。然后贪心模拟每一tick。

期望得分:20~30分

算法二:

考虑最大值最小最小值最大的通法二分答案。

1 1e18的范围内二分最大值,然后用一个check函数判断当前最大值是否可以在S次之类出现。

时间复杂度O(mnlog11018)

期望得分:60分

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define in read()
int n,m,x,s;
int a[100001];
inline int read()
{
	static char ch;
	int res=0,sign=1;
	while((ch=getchar())<'0'||ch>'9'){
		if(ch=='-'){
			sign=-1;
		}
	}
	res=ch-'0';
	while((ch=getchar())>='0'&&ch<='9'){
		res=res*10+ch-'0';
	}
	return res*sign;
}
int ans=0;
inline bool check(int t,int s){
	int cnt=0;
	for(register int i=1;i<=n;i++){
		if(a[i]-s>t) cnt+=(a[i]-s-t-1)/x+1;
		if(cnt>s) return 0;
	}
	return 1;
}
signed main()
{
	n=in,m=in,x=in;
	for(register int i=1;i<=n;i++){
		a[i]=in;
	}
	std::sort(a+1,a+n+1);
	for(register int i=1;i<=m;i++){
		s=in;
		int l=0,r=1e18,mid;
		while(l<r){
			mid=(l+r)>>1;
			if(check(mid,s)){
				ans=mid,r=mid;
			}else{
				l=mid+1;
			}
		}
		printf("%lld\n",ans);
	}
	
	
	
	return 0;
}
算法三:

考虑继续优化算法二。使用主席树维护区间模数,在O(n)的时间找到比v大数。
我们再来变一下式子:

由于蒟蒻不会LaTeX所以只好上图:

大家将就一下:)

由图得知:
我们的cnt就是(tiv)/x向上取整的和,所以变成上图的式子。
考虑先将小麦成熟天数排序,然后再维护整除x的后缀和,倒序插入主席树中。
然后再二分答案就好做了,时间复杂度为O(nlogn+mlog2n)
QWQ

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MX=100005;
const ll oo=1231231231231231;
ll read(){
	static char ch;
	ll res=0,sign=1ll;
	while((ch=getchar())<'0'||ch>'9'){
		if(ch=='-')sign=-1;
	}
	res=ch-'0';
	while((ch=getchar())>='0'&&ch<='9'){
		res=res*10+ch-'0';
	}
	return res*sign;
}

struct node{
	int ls,rs;
	ll x;
};

struct SEGT//主席树封装 
{
	#define mid ((l+r)>>1)
	int cnt;
	node tre[MX*75];
	void upd(int a){
		tre[a].x=tre[tre[a].ls].x+tre[tre[a].rs].x;
	}
	void add(int &a,ll l,ll r,ll p,ll x){
		tre[++cnt]=tre[a];//新建节点 
		a=cnt;
		if(l==r) tre[a].x+=x;
		else if(p<=mid) add(tre[a].ls,l,mid,p,x),upd(a);
		else add(tre[a].rs,mid+1,r,p,x),upd(a);
	}
	ll qur(int a,ll l,ll r,ll ql,ll qr){//询问 
		if(!a) return 0;
		else if(l>=ql&&r<=qr){
			return tre[a].x;
		}else if(qr<l||ql>r){
			return 0;
		}else {
			return qur(tre[a].ls,l,mid,ql,qr)+qur(tre[a].rs,mid+1,r,ql,qr);
		}
	}
	#undef mid
}T;

int n,m;
ll mns;
ll val[MX],id_block[MX],sum_id_block[MX];
int rot[MX];

void init()
{
	for(int i=1;i<=n;i++){
		id_block[i]=val[i]/mns;//用x的次数 
	}
	for(int i=n;i>=1;i--){
		sum_id_block[i]=sum_id_block[i+1]+id_block[i];//计算后缀 
		rot[i]=rot[i+1];//倒着转入主席树中 ,记录比val[i]大的数中的模数量 
		T.add(rot[i],0,mns,val[i]%mns,1);//一颗区间树 ,装区间里面的模数 
	}
}

void input()
{
	n=read(),m=read(),mns=read();
	for(int i=1;i<=n;i++){
		val[i]=read();
	}
	sort(val+1,val+n+1);//顺便排序 
}

ll check(ll tar){
	ll ret=0;
	int p=lower_bound(val+1,val+n+1,tar)-val;//nlogn找到大等于他的最小数 
	ret=sum_id_block[p]-(tar/mns)*(n-p+1);//ti/x+v/x 的和 
	ret+=T.qur(rot[p],0,mns,tar%mns+1,mns);// 比他模数大的数 
	return ret;
}

ll work(ll t){
	ll l=0,r=val[n],ans=0,mid;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(check(mid)>t) l=ans=mid+1;
		else r=ans=mid;
	}
	return max(0ll,ans-t);
}
void work_big()
{
	for(int i=1;i<=m;i++){
		ll t;
		t=read();
		printf("%lld\n",work(t));
	}
	
}
int main(){
	input();//输入 
	init();//装入主席树中 
	work_big();
	
	return 0;
}

第三题:

算法一:用LCA预处理出每一个点对的距离,然后n2查询相加。复杂度O(n2logn)

期望得分:40分

算法二:考虑将每一个点作为根节点,O(n)遍历整棵树,n2求出点对距离。

期望得分: 40分

算法三:点分治(好吧逼装多了好像我会正解一样QWQ

第四题:期望毒瘤题(然鹅洛谷上是蓝题??)

算法一:机智如我之n2枚举,35分到手。

算法二:手摸样例打表,发现n=2k时当p=0,ans=(n1)/2p=1,ans=n1
So 50分到手QWQ.

算法三:还是看大佬的见解吧:
https://www.luogu.com.cn/blog/wlj55/solution-p3898

posted @   SSZX_loser_lcy  阅读(61)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示