[NOI2014] BZOJ 3671 随机数据生成器-贪心

4 篇文章 0 订阅

可能我不适合做NOI题

什么水题都做不了

题目链接:右转进入题目

题目大意:请自行参考原题

题解:

首先把前面随机数据生成的部分完全暴力的敲出来

然而我这部分因为内存开小了不停地出各种奇奇怪怪的bug

到最后都怀疑是不是不适合编程

显然,敲完生成的棋盘之后,可以知道(1,1)和(n,m)是一定要选的。

下一个选什么呢?显然,从1开始考虑到n*m(应该注意到T的值是从1~n*m),如果能选就选。

因为要从小到大考虑,所以要对T排序后记录一下排名。

具体点说,如果选择了(i,j)这个点,那么它的左下角和右上角都不能选了,就把他们覆盖掉。

所以可以用一个支持矩阵加和单点修改的BIT。复杂度O(n^2lg^2n)

加上一开始排序的快排的O(n^2lgn),总复杂度O(n^2lg^2n)。

不好

考虑优化。

注意到程序瓶颈在BIT上。

注意到每个点无论被覆盖了多少次,只要被覆盖了就不能选

注意到每次覆盖操作都是一个前缀矩阵。

换句话说,如果一个点被覆盖了,(暂且只考虑左下角的矩阵),那么他的左下角一定都被覆盖了。

换句话说,当我们进行覆盖操作的时候(还是只考虑(i,j)左下角),从i+1行到n行,每行从右到左染色,

如果染色到一个已经覆盖的点就停止。

特别的如果(i+1,j-1)也被覆盖了,这次覆盖操作就不用进行了。

由于每个店至多被覆盖一次,所以覆盖的复杂度是O(MN)的。

而注意到T的值是从1~MN的,所以可以用桶排

总复杂度O(MN)。

这个题在BZOJ上卡PE,MLE,NOI的时候还卡常数……

我也真是醉了……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define MAXN 5010
#define MAXM 5010
#define idx(i) ((i-1)/m+1)
#define idy(i) (i-(idx(i)-1)*m)
#define ull long long
using namespace std;
int t[MAXN*MAXM],id[MAXN*MAXM],cnt;
bool chess[MAXN][MAXM];
priority_queue<int> q;
inline void getnum(int &x)
{
	x=0;char ch;
	while((ch=getchar())<'0'||ch>'9');
	x=ch^'0';
	while((ch=getchar())>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch-'0');
	return;
}
int main()
{
	ull x,a,b,c,d;cnt=0;int n,m,Q;
	int _x,_a,_b,_c,_d;
	scanf("%d%d%d%d%d",&_x,&_a,&_b,&_c,&_d);
	x=_x;a=_a;b=_b;c=_c;d=_d;
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=n*m;i++)
		t[i]=i;
	for(int i=1;i<=n*m;i++)
	{
		x=(a*x%d*x%d+b*x%d+c)%d;
		swap(t[i],t[int(x%i)+1]);
	}
	while(Q--)
	{
		int u,v;scanf("%d%d",&u,&v);
		swap(t[u],t[v]);
	}
	for(int i=1;i<=n*m;i++)
		id[t[i]]=i;
	chess[1][1]=true;q.push(-t[1]);cnt++;
	chess[n][m]=true;q.push(-t[n*m]);cnt++;
	for(int i=1;i<=n*m&&cnt<n+m-1;i++)
	if(!chess[idx(id[i])][idy(id[i])])
	{
		q.push(-i);cnt++;
		for(int j=idx(id[i])+1;j<=n;j++)
			if(idy(id[i])-1>=1&&chess[j][idy(id[i])-1]) break;
			else for(int k=idy(id[i])-1;k>=1;k--)
				if(chess[j][k]) break;
				else chess[j][k]=true;
		for(int j=idx(id[i])-1;j>=1;j--)
			if(idy(id[i])+1<=m&&chess[j][idy(id[i])+1]) break;
			else for(int k=idy(id[i])+1;k<=m;k++)
				if(chess[j][k]) break;
				else chess[j][k]=true;
	}
	while(!q.empty())
	{
		int x=-q.top();q.pop();
		if(!q.empty()) printf("%d ",x);
		else printf("%d\n",x);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值