3671: [Noi2014]随机数生成器

其实这题真的很水。。。。。。一眼就能看出来解法。

但是

但是

但是

坑~坑~坑~坑~坑~坑啊。

首先模拟一遍出变换后的排列,时间O(N*M+Q),扫一遍排列,用x[i],y[i]记录排列中值为i的数在棋盘的(x[i],y[i])的位置,然后从1开始枚举,每次判断该数能否放在路径序列里,贪心一遍就可以了。

然而这里有两个坑:

①不能建x,y数组,三个5000*5000的数组就炸内存了,所以,设rank[i]为在排列T中值为i的数的下标,然后通过下标算出x,y。

②判断该数能否放在路径序列里,要判断两点,即该数在棋盘左下方不能有数(之前放上去的数),在右上方也不行,然后这里我很SX地用树状数组去算左下和右上有多少个数,O(N*M*logN*M)果断TLE了。其实直接暴力就可以了,每次在棋盘上放一个数的时候更新它的影响,即棋盘上不能再放数的位置。

这样就变成了O(N*M*N*M),Σ( ° △ °|||)︴好像有什么不对的地方。

哦对,加一个剪枝,在更新的时候如果该行(该列)不能更新,直接退出,因为更新的矩形必定是连续且一直到尽头的,所以这判断了后面的矩形有木有被更新,于是就变成了O(N*M)。

然后就过了。33秒多。。。。。。。。不过代码好短啊

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=5000+5;
typedef long long ll;
int n,m,ans[N*2],rank[N*N],T[N][N];
int a,b,c,d;
void add(int x,int y){
	for(int i=x+1;i<=n;i++){
		bool flag=true;
		for(int j=y-1;j>=1;j--)
		if(T[i][j])break;
		else{
			T[i][j]=1;
			flag=false;
		}
		if(flag)break;
	}
	for(int i=x-1;i>=1;i--){
		bool flag=true;
		for(int j=y+1;j<=m;j++)
		if(T[i][j])break;
		else{
			T[i][j]=1;
			flag=false;
		}
		if(flag)break;
	}
}
inline int f(ll x){
	return (a*x*x+b*x+c)%d;
}
inline int x(int s){
	return (s-1)/m+1;
}
inline int y(int s){
	return (s-1)%m+1;
}
int main(){
	int last,q,u,v;scanf("%d%d%d%d%d%d%d%d",&last,&a,&b,&c,&d,&n,&m,&q);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)T[i][j]=(i-1)*m+j;
	for(int i=1;i<=n*m;i++){
		u=i;v=f(last)%i+1;
		swap(T[x(u)][y(u)],T[x(v)][y(v)]);
		last=f(last);
	}
	while(q--){
		scanf("%d%d",&u,&v);
		swap(T[x(u)][y(u)],T[x(v)][y(v)]);
	}
	for(int i=1;i<=n*m;i++)rank[T[x(i)][y(i)]]=i;
	int cnt=0;
	memset(T,0,sizeof(T));
	for(int i=1;i<=n*m;i++){
		if(!T[x(rank[i])][y(rank[i])]){
			ans[++cnt]=i;
			add(x(rank[i]),y(rank[i]));
			if(cnt==n+m-1)break;
		}
	}
	for(int i=1;i<=cnt;i++)
	if(i!=1)printf(" %d",ans[i]);
	else printf("%d",ans[i]);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值