P4864 Jerry Loves Lines

Captain_Paul

2018-09-04 19:01:25

Personal

题意:给定$n$个一次函数,询问在某一位置的第$k$大函数值 第$k$大?难道是主席树?好像并不是。。 有一个比较明显的结论:若两条直线相交,则在交点后的部分两条直线的大小关系与之前相反(貌似是初中数学) 由此,将询问离线存储,按照$x$大小排序 然后算出$n$条直线在最左端的函数值,根据这个值排序 $n^2$枚举,算出直线的两两交点 (在按照左端函数值排好序的情况下,如果后面直线在最右端的函数值比前面小,则两直线相交) 将每两条相交的直线放到结构体中,按照交点的位置从小到大排序 之后可以处理询问 对于每一个询问,对它有影响的就是交点在它之前并且在上一个询问之后的直线 用$rk$表示每一条直线在此处的排名,$num$表示每个排名对应的直线 显然两条直线相交只会使各自的排名变化$1$ 询问的答案即为$x$处排名为$k$的函数值 ``` #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #define reg register using namespace std; typedef long long ll; const int N=2005,M=5e5+5; struct line { int k,b,id; ll y; inline friend bool operator < (line a,line b) {return a.y<b.y;} }c[N]; struct Q { int x,id; inline friend bool operator < (Q a,Q b) {return a.x<b.x;} }q[M]; struct node { int x,y; ll p; inline friend bool operator < (node a,node b) { return a.p==b.p?a.x<b.x:a.p<b.p; } }t[2000005]; int n,m,k,cnt,num[N],rk[N]; ll ans[M]; inline int read() { int x=0,w=1; char c=getchar(); while (!isdigit(c)&&c!='-') c=getchar(); if (c=='-') c=getchar(),w=-1; while (isdigit(c)) { x=(x<<1)+(x<<3)+c-'0'; c=getchar(); } return x*w; } inline ll calc(int x,int k){return (ll)c[k].k*x+c[k].b;} inline ll getpos(int p,int q){return (ll)(c[q].b-c[p].b)/(c[p].k-c[q].k)+1;} int main() { n=read(),m=read(),k=read(); for (reg int i=1;i<=n;i++) c[i].k=read(),c[i].b=read(); for (reg int i=1;i<=m;i++) q[i].x=read(),q[i].id=i; sort(q+1,q+m+1); for (reg int i=1;i<=n;i++) c[i].y=calc(q[1].x,i); sort(c+1,c+n+1); for (reg int i=1;i<=n;i++) { c[i].id=num[i]=rk[i]=i; for (reg int j=i+1;j<=n;j++) if (c[i].k!=c[j].k&&calc(q[m].x,i)>=calc(q[m].x,j)) { ll pos=getpos(i,j); if (pos>=q[1].x) t[++cnt]=(node){i,j,pos}; } } sort(t+1,t+cnt+1); for (reg int i=1,now=1;i<=m;i++) { while (now<=cnt&&t[now].p<=q[i].x) { num[++rk[t[now].x]]=t[now].x; num[--rk[t[now].y]]=t[now].y; ++now; } ans[q[i].id]=calc(q[i].x,num[k]); } for (reg int i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; } ```