EZ 2018 04 13 NOIP2018 模拟赛(八)

这次的题目都是什么鬼?

玄学乱搞+肉眼看CODE+倒着搜索?

好吧是我ZZ了

链接在此

T1 玄学乱搞

由于考场上写的部分分做法忘记讨论n<=2000时的情况,少得了30pts

很容易得到一个基于排序的算法:

  • 记录每个数的值以及编号

  • 按编号将原数组从大到小排序

  • 对于每一个数,用在它之前的编号最大和最小的分别更新ans

对于这个算法的正确性显然。因为之前所有点的值都比它大,所以可以稳定确立下min(h[i],h[j])的值,然后记录编号的方式就OK了

但是这里的数据是1e7,快拍时间复杂度为O(n log2 n)要GG的

所以很自然地联想到基数排序,由于我姿势水平有限,像LowestJN dalao那样高级的基排,所以只会按位比较的那种

极限复杂度为:O(n log10 max{a[i]})

当n=1e7,max{a[i]}=1e9时,总操作数为9e7≈1e8

理论上机子快一点,常数小一点就可以卡过去了

然而我太蒟了所以被卡

这里贴一下Radix_sort的CODE以供参考:

#include<cstdio>
#include<cstring>
const int N=1e7+5;
using namespace std;
typedef long long LL;
struct data
{
	int x,id;
}a[N],temp[N];
int cnt[15],n,d,MIN,MAX;
LL ans=-1;
inline char tc(void)
{
	static char fl[100000],*A=fl,*B=fl;
	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
	x=0; char ch=tc();  
	while (ch<'0'||ch>'9') ch=tc();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline int int_max(int a,int b)
{
	return a>b?a:b;
}
inline int int_min(int a,int b)
{
	return a<b?a:b;
}
inline int abs(int x)
{
	return x>0?x:-x;
}
inline LL LL_max(LL a,LL b)
{
	return a>b?a:b;
}
inline int getbit(int x)
{
	int tot=0;
	if (!x) return 1;
	while (x) ++tot,x/=10;
	return tot;
}
inline void radix_sort(int n)
{
	register int i,j;
	int radix=1;
	for (i=1;i<=d;++i,radix*=10)
	{
		memset(cnt,0,sizeof(cnt));
		for (j=1;j<=n;++j)
		++cnt[(a[j].x/radix)%10];
		for (j=8;j>=0;--j)
		cnt[j]+=cnt[j+1];
		for (j=n;j>=1;--j)
		temp[cnt[(a[j].x/radix)%10]--]=a[j];
		memcpy(a,temp,sizeof(a));
	}
}
int main()
{
	register int i;
	//freopen("A.in","r",stdin); freopen("A.out","w",stdout);
	for (read(n),i=1;i<=n;++i)
	read(a[i].x),a[i].id=i,d=int_max(d,getbit(a[i].x));
	radix_sort(n);
	for (MIN=MAX=a[1].id,i=2;i<=n;++i)
	ans=LL_max(ans,(LL)int_max(abs(a[i].id-MIN),abs(a[i].id-MAX))*a[i].x),MIN=int_min(MIN,a[i].id),MAX=int_max(MAX,a[i].id);
	printf("%lld",ans);
	return 0;
}

好吧我们来看标算:

由于我们发现从左往右这种传统的扫描方式难以得出答案,所以我们考虑从两边向中间寻找

设立l,r表示目前两端的区间范围,所以我们可以用:

ans=max(ans,(r-l)*min(h[l],h[r]))

来更新ans

由于min这个操作,为了贪心我们把大的那个数留下来到下一次,即

if (h[l]<h[r]) ++l; else --r;

这样就实现了O(n)的复杂度

CODE

#include<cstdio>
using namespace std;
typedef long long LL;
const int N=1e7+5;
int a[N],l,r,n;
LL ans;
inline char tc(void)
{
	static char fl[100000],*A=fl,*B=fl;
	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
	x=0; char ch=tc();
	while (ch<'0'||ch>'9') ch=tc();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline LL max(LL a,LL b)
{
	return a>b?a:b;
}
inline int min(int a,int b)
{
	return a<b?a:b;
}
int main()
{
	register int i;
	//freopen("A.in","r",stdin); freopen("A.out","w",stdout);
	for (read(n),i=1;i<=n;++i)
	read(a[i]);
	l=1; r=n;
	while (l<r)
	{
		ans=max(ans,(long long)(r-l)*min(a[l],a[r]));
		if (a[l]<a[r]) ++l; else --r;
	}
	printf("%lld",ans);
	return 0;
}

T2 肉眼看CODE

我是真的蒙蔽了

首先%%%XZTdalao肉眼看拿了70+pts

还有一群初三dalao直接A了此题(据说有人花了2H在看这个)

不过不知道标算是什么

听他们说有一个比较可行的算法,就是先rand()一组方案,然后看一下得分。然后根据得分来修改算法

不过可能要惊人的脚本使用技术耐心

话说我只认出了姜度dalao的代码,有些人还认出了楼神Manchery

实为佩服,只能说我输了

T3 倒着搜索

这道题其实真的很简单。

我们先写好DFS爆搜,然后剪剪枝

接下来开始打表

然后把枚举的顺序由从小到大改为从大到小即可成功A掉此题(快得飞起)

这也是什么搜索技巧吗?

正解:Dancing Link O(n)找规律

爆搜CODE

#include<cstdio>
#include<cstdlib>
using namespace std;
int num[55],n,m;
inline void print(void)
{
	for (register int i=1;i<=m;++i)
	if (!num[i]) putchar('-'); else putchar(num[i]+'A'-1);
	exit(0);
}
inline void DFS(int now)
{
	if (!now) print();
	for (register int i=1;i<=m;++i)
	if (!num[i])
	{
		num[i]=now;
		if (i+now+1<=m&&!num[i+now+1]) num[i+now+1]=now,DFS(now-1),num[i+now+1]=0;
		if (i-now-1>=1&&!num[i-now-1]) num[i-now-1]=now,DFS(now-1),num[i-now-1]=0;
		num[i]=0;
	}
}
int main()
{
	scanf("%d",&n); m=n<<1|1;
	DFS(n);
	return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/8832886.html