比赛地址: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=x1−x2y1−y2
(虽然一开始我以为
k
=
x
1
−
x
2
y
1
−
y
2
k=\cfrac{x_1-x_2}{y_1-y_2}
k=y1−y2x1−x2,但是发现过不了样例后就改了,这就是猜结论的隐患)
接着对于平面直角坐标系上的每个点,我们都作一条经过它的斜率为
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
我比赛时的做法其实就是正解,只是精度爆炸了而已。
计算一下:
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;
}