2019.08.17【NOIP提高组】模拟 A 组 比赛总结

比赛地址:http://jzoj.net/senior/#contest/home/2852

题目

jzoj 6290. 倾斜的线

http://jzoj.net/senior/#contest/show/2852/0

jzoj 6305. 最小值

http://jzoj.net/senior/#contest/show/2852/1

jzoj 6307. 安排

http://jzoj.net/senior/#contest/show/2852/2


过程

今天爆炸了……真是开心 ψ(`∇´)ψ
第一题一看数据,哇!暴力有50分耶!我知足了极限也只有 1 0 5 10^5 105,肯定是 l o g log log的解法啦!
冥冥之中,我总觉得是排序
但是我好像连直线的斜率都不会求啊!
没关系,我们可以猜结论,联系DP斜率优化一次函数的知识,我们可以猜出
k = y 1 − y 2 x 1 − x 2 k=\frac{y_1-y_2}{x_1-x_2} k=x1x2y1y2
(虽然一开始我以为 k = x 1 − x 2 y 1 − y 2 k=\cfrac{x_1-x_2}{y_1-y_2} k=y1y2x1x2,但是发现过不了样例后就改了,这就是猜结论的隐患)
接着对于平面直角坐标系上的每个点,我们都作一条经过它的斜率为 P / Q P/Q P/Q的直线:
在这里插入图片描述
对于A点而言,最接近斜率的点是C;对于B点而言,最接近的是D……
它们都是与直线纵坐标之差最小的点,因此可以猜出结论:

对于一个点而言,与它所连直线斜率最接近 P / Q P/Q P/Q的点必定是与经过它,斜率为 P / Q P/Q P/Q的直线纵坐标值差最小的点。

于是排个序, O ( n ) O(n) O(n)判断就可以了。总时间复杂度 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)

第二题显然是DP。
我设 g i g_i gi表示点 i 处是区间右端点的最大价值和,接着就可以 O ( n 2 ) O(n^2) O(n2)转移了。
这样显然会TLE,因此考虑优化。
通常这种题目都是用斜率优化或是单调队列,然而我怎么也没有想到如何优化。
最后脑子糊糊的,感觉可以用单调栈,于是就敲了起来。
结果弄了很久也没有弄出来,好不容易过了样例,我注意到A,B,C,D可能是负数,于是就出了个负数的数据。
然后惊奇的发现输出经一模一样!
试了几个都是如此,感觉这个方法不行啊!于是打了 O ( n 2 ) O(n^2) O(n2)的DP交了上去,接着验证——还是一样的!
神奇!
比赛结束前两分钟才发现是读入优化没判负号……
险些爆〇了。

第三题什么神仙?!

总分:65+30+0=95(T1炸掉了)。


题解

T1

我比赛时的做法其实就是正解,只是精度爆炸了而已。
在这里插入图片描述
计算一下:

扫描二维码关注公众号,回复: 11879371 查看本文章

48840271/140346761 = 0.34799713689153111271303225872095
241818102/694885321 = 0.34799713663832020996166646611319

原来是因为我判断两个long double是否相同时是if(x-y>1e-9),把这个值改成1e-14就好了。

T2

正解就是单调栈优化DP(真香)。
可以按最小值把原序列划分成几段,用单调栈来维护这些段。
段内的值用一个数组存一下就好了,至于用哪一个更新答案,用前缀数组来维护。

T3

怎么又是老虎和蒜头……
作者一开始就知道这题很难了:
在这里插入图片描述
键盘可以吃吗?
百度一下,发现还有烹饪教程(麻婆键盘):
在这里插入图片描述
哎呀,想太多了。
其实这题是一道很新奇的题目。
发现A→B=A→C→B,因此可以把A排序变成C,再把C变成B。
由于操作可逆,我们可以把过程变成把A,B都排序。
这样就可以分开处理了。
17%的做法是直接暴力求逆序对,交换。
这样操作数最多是 n 2 n^2 n2的,绝对不会超过345678,因此这种方法是对的。
由逆序对的思路容易联想到归并排序(逆序对不就这么求的嘛!),考虑如何归并排序。
我们现在有两个有序序列(画工丑陋,不必在意):
在这里插入图片描述
[ l , m i d ] [l,mid] [l,mid]的后x个和 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]的前x个,使得 [ l , m i d ] [l,mid] [l,mid]的后x个都大于 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]的前x个(x要尽可能多取):
在这里插入图片描述
然后想办法让这两个序列交换位置(方法后面会提到):
在这里插入图片描述
接着再处理右上部分和左下部分就可以了,这样最后会把它们分别合成一个序列,合起来就可以了。
实现的话就是用归并排序向下处理,快速排序将两个序列并成一个序列。
实现起来细节多多。
真·神仙!


总结

比赛的时候要勇于猜结论,多注意细节!


代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define llf long double
#define e 1e-14
#define N 200005
struct node{
    
    ll x,y;}a[N];
char ch;ll P,Q;
llf solpe,ans=1e10,temp;
inline char gc()
{
    
    
	static char buf[500000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,500000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(ll &x)
{
    
    
	while(ch=gc(),ch<'0'||ch>'9');x=ch-'0';
	while(ch=gc(),ch>='0'&&ch<='9') x=x*10+ch-'0';
}
inline llf abss(llf k){
    
    return k>0?k:-k;}
inline bool cmp(node x,node y)
{
    
    return x.y*Q-x.x*P<y.y*Q-y.x*P;}
inline ll gcd(ll x,ll y)
{
    
    
	ll r;while(y)
		r=x%y,x=y,y=r;
	return x;
}
int main()
{
    
    
	freopen("slope.in","r",stdin);
	freopen("slope.out","w",stdout);
	ll n,i,j,x,y,p,q;
	read(n),read(P),read(Q);
	solpe=P/(llf)Q;
	for(i=1;i<=n;i++) read(a[i].x),read(a[i].y);
	sort(a+1,a+n+1,cmp);
	for(i=1;i<n;i++)
	{
    
    
		x=a[i+1].x-a[i].x,y=a[i+1].y-a[i].y;
		if((x<0)!=(y<0)) continue;
		if(x<0) x=-x,y=-y;
		j=gcd(x,y),x/=j,y/=j;
		temp=abss(y/(llf)x-solpe);
		if(ans-temp>e) ans=temp,p=y,q=x;
	}
	printf("%lld/%lld\n",p,q);
	return 0;
}

T2

#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define N 200005
ll a[N],f[N],g[N],stack[N][2],pre[N];
char ch;
inline char gc()
{
    
    
	static char buf[500000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,500000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(ll &x)
{
    
    
	char ch;bool b=0;
	while(ch=gc(),ch!='-'&&(ch<'0'||ch>'9'));
	if(ch=='-') b=1,ch=gc();x=ch-48;
	while(ch=gc(),ch>='0'&&ch<='9') x=x*10+ch-48;
	if(b) x=-x;
}
int main()
{
    
    
	freopen("min.in","r",stdin);
	freopen("min.out","w",stdout);
	ll n,i,A,B,C,D,top=1,max;
	read(n),read(A),read(B),read(C),read(D);
	for(i=1;i<=n;i++) read(a[i]),
		f[i]=A*a[i]*a[i]*a[i]+B*a[i]*a[i]+C*a[i]+D;
	for(i=1;i<=n;i++)
	{
    
    
		for(max=g[i-1];top&&a[stack[top][1]]>a[i];top--)
			if(max<stack[top][0]) max=stack[top][0];
		stack[++top][0]=max,stack[top][1]=i;
		if(top>1) pre[top]=pre[top-1]>max+f[i]?pre[top-1]:max+f[i];
		else pre[top]=max+f[i];
		g[i]=pre[top];
	}
	printf("%lld\n",g[n]);
	return 0;
}

T3

#include<cstdio>
using namespace std;
#define N 5005
int a[N],ans[2][345680][2],cnt[2];
int flag=1;
inline void swap(int &x,int &y){
    
    x^=y,y^=x,x^=y;}
void qsort(int l,int mid,int r)
{
    
    
	if(l>mid||mid>=r) return;
	int i=mid,j=mid+1,k,t;
	while(l<i&&j<r&&a[i-1]>a[j+1]) i--,j++;
	if(a[i]>a[j])
	{
    
    
		for(k=i,t=mid;k<t;k++,t--)
			ans[flag][++cnt[flag]][0]=k,
			ans[flag][cnt[flag]][1]=t,
			swap(a[k],a[t]);
		for(k=mid+1,t=j;k<t;k++,t--)
			ans[flag][++cnt[flag]][0]=k,
			ans[flag][cnt[flag]][1]=t,
			swap(a[k],a[t]);
		for(k=i,t=j;k<t;k++,t--)
			ans[flag][++cnt[flag]][0]=k,
			ans[flag][cnt[flag]][1]=t,
			swap(a[k],a[t]);
		if(l<mid) qsort(l,i-1,mid);
		if(mid+1<r) qsort(mid+1,j,r);
	}
}
void msort(int l,int r)
{
    
    
	int mid=l+r>>1;
	if(l<mid) msort(l,mid);
	if(mid+1<r) msort(mid+1,r);
	qsort(l,mid,r);
}
int main()
{
    
    
	freopen("swap.in","r",stdin);
	freopen("swap.out","w",stdout);
	int i,n;
	scanf("%d",&n);
	loop:
	for(i=1;i<=n;i++) scanf("%d",a+i);
	msort(1,n);
	if(flag){
    
    flag=0;goto loop;}
	printf("%d\n",cnt[1]+cnt[0]);
	for(i=1;i<=cnt[1];i++) printf("%d %d\n",ans[1][i][0],ans[1][i][1]);
	for(i=cnt[0];i;i--) printf("%d %d\n",ans[0][i][0],ans[0][i][1]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huangzihaoal/article/details/99697245
今日推荐