@LittleRewriter
2017-10-08T02:23:04.000000Z
字数 4735
阅读 956
题解 爆零
数据太弱暴力AC.jpg
比如向右看 5 6 7 这样单调递增
而维护这个东西就是只有一段插入,没有删除->单调栈
而查询最近的由于要删掉小的所以就是top减一这个位置。
从右向左:
for(int i=n;i>=1;--i){while(r&&s[r]<=h[i]) r--b[q[r]]+=a[i];//b[i]为第i个人收到的玫瑰数s[++r]=h[i];q[r]=i;//整个栈中第r个是i}
从左向右反过来就行。
复杂度O(n)
#include <cmath>#include <cstdio>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;int n,s[50002],d[50002],ans[50002],ANS,a[50002],b[50002],r,i;int main(){freopen("treasure.in","r",stdin);freopen("treasure.out","w",stdout);cin>>n;for (i=1; i<=n; i++) scanf("%d%d",&a[i],&b[i]);s[1]=a[1]; d[1]=1; r=1;for (i=2; i<=n; i++){while (r!=0 && a[i]>s[r]) { ans[i]+=b[d[r]]; r--; }r++;s[r]=a[i];d[r]=i;}s[1]=a[n]; d[1]=n; r=1;for (i=n-1; i>=1; i--){while (r!=0 && a[i]>s[r]) { ans[i]+=b[d[r]]; r--; }r++;s[r]=a[i];d[r]=i;}for (i=1; i<=n; i++) ANS=max(ANS,ans[i]);cout<<ANS;return 0;}
暴搜点
如果是长方形,那么一定是和糖果贴紧的
对于正方形,先离散化,然后枚举上边、下边、左边再枚举右边
最后将长方形变成正方形。
离散化
struct node {int x,y;} t[10005];int cmp(node i,ndoe j) {i.y<j.y;}for (i=1; i<=n; i++) cin>>a[i];for (i=1; i<=n; i++) t[i].x=i,t[i].y=a[i];sort(t+1,t+n+1,cmp);for (i=1; i<=n; i++){if (t[i].y!=t[i-1].y) now++;p[now]=a[t[i].x]; a[t[i].x]=now;}
离散化之后为n*n,直接枚举即可
枚举上边、下边,判断左边是1的情况下右边是什么
由于是个正方形,随着左边的向右移动,右边一定向右移动,直接枚举即可。
假如x可行,那么x+1能覆盖C个糖果,x-1不行。
所以答案是连续的,那么我们可以二分答案。
问题就是check()函数。
枚举上边,下边位置是固定的。那么就能确定哪些糖果被夹在区间中,而左边的移动带来右边的移动,如果用前缀和维护就可以O(n)判断
#include <cmath>#include <cstdio>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;struct node {int x,y;} a[1005];int C,n,L,R,mid,b[1005],o,i;int cmp(node i,node j) {return i.x<j.x;}int CMP(int i,int j) {return i<j;}bool WORK(int l,int r){if (r-l+1<C) return false; o=0;for (int i=l; i<=r; i++) b[++o]=a[i].y;sort(b+1,b+o+1,CMP);for (int i=C; i<=o; i++)if (b[i]-b[i-C+1]<=mid) return true;return false;}bool OK(int x){int l=1;for (int i=1; i<=n; i++){if (a[i].x-a[l].x>x){if (WORK(l,i-1)) return true;while (a[i].x-a[l].x>x) l++;}}if (WORK(l,n)) return true;return false;}int main(){freopen("square.in","r",stdin);freopen("square.out","w",stdout);scanf("%d%d",&C,&n);for (i=1; i<=n; i++)scanf("%d%d",&a[i].x,&a[i].y);sort(a+1,a+n+1,cmp);L=0; R=10000; mid=(L+R)/2;while (L<=R){if (OK(mid)) {R=mid-1; mid=(L+R)/2;} else{L=mid+1;mid=(L+R)/2;}}cout<<L+1;return 0;}
计算几何
手算即可
做出来s-t图。
答案就是最大值减去最小值
也就是绿色的东西
观察可知绿色在交点处似乎可以取到最值。怎么证明?我们使用反证法:
假设在某一个不是交点的位置取到了最值,那么我们一定能找到其它点更小。
所以自然而然的,求出所有交点,求出每只豹子的位置,更新答案。
蛤?
后来时刻只有进行追逐的才有意义,否则出发晚并且速度慢的就没意义。
因此只有这些追逐的交点才是有意义的。也就是求图像上界与下界。

如图,对于这两条线,在后面的时候斜率大的占领靠上的。
那么按照斜率排序,每插入一条线就会将上界的一段后缀覆盖掉。也就是插入并赋值这个操作是O(1)的。
下界同理,由于我们可以维护出交点和对应的解析式,这个时候搞一个双指针:上追到下就让下移动,下追到上就让上移动。这样就可以O(n)跑完。
用栈维护一开始保证跑的慢的豹子一定先跑,跑的快的一定后跑
也就是:
上凸壳:对于两只豹子,一只跑得慢(v小)且后来才跑(t大),一只跑得快(v大)且一开始就跑(t小),我们保留后面那只。
下凸壳:对于两只豹子,一只跑得慢且后来才跑,一只跑得快且一开始就跑,我们保留前面那只。
这样预处理一下。
找见上凸壳与下凸壳,那么要求的东西一定是先变小后变大(上凸壳斜率增加,下凸壳减小)
也就是近似抛物线的一个东西
那么就可以二分
check()函数:
上凸壳斜率小于下面则往右,否则往左
斜率可以定义法求导或者直接维护一下得到。
也可以用神奇的三分做法
//ZHW:这个标程写的很sabi#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<iostream>#include<algorithm>using namespace std;const long double INF=(long double)1000000000*10;long double L,R,mid,ans,hh[100005];int r,rr,i,n,MAX,X,Y,cnt,vv[100005],vv2[100005];struct node2 {int t; long double l;} s[200005],S[200005];struct node {int t,v;} t[100005];int cmp(node i,node j) {return i.v<j.v || i.v==j.v && i.t>j.t;}struct Node {long double x;int y,z;} p[200005];int CMP(Node i,Node j) {return i.x<j.x;}long double work(int x,long double y) {return (long double)t[x].v*y-hh[x];}int main(){freopen("chase.in","r",stdin);freopen("chase.out","w",stdout);while (1){scanf("%d",&n);// if (n==0) return 0;MAX=0;for (i=1; i<=n; i++){scanf("%d%d",&t[i].t,&t[i].v);MAX=max(MAX,t[i].t);}sort(t+1,t+n+1,cmp); int MIN=t[n].t;for (i=n-1; i>=2; i--){if (t[i].t>MIN) vv[i]=1; elseMIN=t[i].t,vv[i]=0;}for (i=1; i<=n; i++) hh[i]=(long double)t[i].t*t[i].v;r=1; s[1].l=MAX; s[1].t=1; s[2].l=INF; vv[n]=0;for (i=2; i<=n; i++)if (!vv[i]){while (r && work(i,s[r].l)>=work(s[r].t,s[r].l)) r--;if (!r) {r=1; s[1].l=MAX; s[1].t=i; continue;}L=s[r].l; R=s[r+1].l; mid=(L+R)/2.0;for (int I=1; I<=80; I++){if (work(i,mid)>=work(s[r].t,mid)) {R=mid; mid=(L+R)/2.0;} else {L=mid; mid=(L+R)/2.0;}}s[++r].l=mid; s[r].t=i; s[r+1].l=INF;}rr=1; S[1].l=MAX; S[2].l=INF; S[1].t=n;MIN=t[1].t;for (i=2; i<n; i++)if (t[i].t<MIN) vv2[i]=1; elseMIN=t[i].t,vv2[i]=0;for (i=n-1; i>=1; i--)if (!vv2[i]){while (rr && work(i,S[rr].l)<=work(S[rr].t,S[rr].l)) rr--;if (!rr) {rr=1; S[1].l=MAX; S[1].t=i; continue;}L=S[rr].l; R=S[rr+1].l; mid=(L+R)/2.0;for (int I=1; I<=80; I++){if (work(i,mid)<=work(S[rr].t,mid)) {R=mid; mid=(L+R)/2.0;} else {L=mid; mid=(L+R)/2.0;}}S[++rr].l=mid; S[rr].t=i; S[rr+1].l=INF;}cnt=0;for (i=1; i<=r; i++) {p[++cnt].x=s[i].l; p[cnt].y=1; p[cnt].z=s[i].t;}for (i=1; i<=rr; i++) {p[++cnt].x=S[i].l; p[cnt].y=0; p[cnt].z=S[i].t;}sort(p+1,p+cnt+1,CMP); X=Y=0; ans=INF;for (i=1; i<=cnt; i++)//X为上凸壳指针,y为下凸壳指针{if (p[i].y==1) X=p[i].z; else Y=p[i].z;// printf("%.5f\n",(double)p[i].x);if (X && Y) ans=min(ans,work(X,p[i].x)-work(Y,p[i].x));}printf("%.2f\n",fabs((double)ans));return 0;}}