E题:卿学姐与城堡的墙
有N条直线,以y=kx+b的形式给出,问有多少种方式任取两个直线,使这两个直线的交点在x=u和x=v之间
对于两个直线a和b,怎么判断他们的交点在x=u和x=v之间呢
假如直线a在x=u时的纵坐标为yau,在x=v时的纵坐标为yav,直线b在x=u时的纵坐标为ybu,在x=v时的纵坐标为ybv。假设yau在ybu的下面,但是yav却在ybv的上面,那中间就是一定有交点的,反之如果yau在ybu上面,但是yav却在ybv下面,也是一定有交点的,当然还有yau==ybu和yav==ybv两种情况。
首先不考虑相等的情况,就是可以把直线按yu升序排序,然后把yv离散化,然后用树状数组维护yv,然后对于每一个yv,大概思路就是查询它的树状数组的前缀和看看前面有多少个比它小的,用总数减一下就可以知道有多少个比它大的,就肯定存在那么多交点(就是相当于逆序对)。
然后我们再继续考虑细节,首先可以按yu升序排序,然后对于每条直线,如果它和上条直线的yu相同,那我们记录这个yu的值的最早出现的位置,假设为last,当前位置为i,则ans +=i-last,因为它可以和前面所有相同的都构成交点,
那么就是考虑右边是否有相同的,右边的话,我们肯定是找大于等于当前yv的数有几个,肯定就是有几个交点,然后加入当前是第i条直线,那么之前有i条直线,然后先树状数组统计小于yv的有几条,假设为x条,那么ans+=i-x,然后树状数组更新当前的yv就行
数据水了,貌似没有在有多条直线交在x=u的同一个点和或者交在x=v上的同一个点的情况,各种姿势都能花式水过去
代码:
#include <cstdio> #include <cstdlib> #include <algorithm> using namespace std; #define ll long long #define maxn 200005 struct Line { ll y1, y2; bool operator < (Line x)const { return y1 < x.y1; } }line[maxn]; int N; ll u, v, data[maxn]; ll tree[maxn]; void update(int i, ll val) { while (i <= N) { tree[i] += val; i += i&(-i); } } ll query(int i) { ll sum = 0; while (i) { sum += tree[i]; i -= i&(-i); } return sum; } int main() { //freopen("input.txt", "r", stdin); scanf("%d%lld%lld", &N, &u, &v); ll a, b; for (int i = 0; i < N; ++i) { scanf("%lld%lld", &a, &b); line[i].y1 = a*u + b, line[i].y2 = a*v + b; data[i] = line[i].y2; } sort(line, line + N); sort(data, data + N); int num = unique(data, data + N) - data; for (int i = 0; i < N; ++i) line[i].y2 = lower_bound(data, data + num, line[i].y2) - data + 1; ll ans = 0; int last = 0; for (int i = 0; i < N; ++i) { if (i > 0 && line[i].y1 == line[i - 1].y1) { //++ans; ans += i - last; } else { ans += i - query(line[i].y2 - 1); last = i; } update(line[i].y2, 1); } printf("%lld\n", ans); //system("pause"); //while (1); return 0; }