链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3574
题意:一个坐标系,给出x1、x2限定左右边界,有n条直线,告诉每条直线的k和b,问在x1、x2区间内空间被直线分割成几部分
思路:
这道题是比赛时做的,AC之后发现别人都是用归并排序求逆序对数来解的。
说我的解法吧,首先拿到题的时候发现是划分区域这样的,第一下就想到了欧拉公式,但是n有30000,n^2找交点肯定要超时。土豪在纸上画了一下,一条直线必然在x1和x2处留下两点,如果一个直线左端点在另一个直线左端点上面,右端点在另一条线右端点下面,则他们必有一个交点。于是可以转换成线段树来做,给左端点从小到大排序,查询比当前直线右端点大的点的数目,更新欧拉公式要用到的点数和边数,注意交点在x1、x2时的情况。由于欧拉公式适用于连通图,所以要借助x1、x2两条边界线,但是直线没有端点,再虚拟出上下两个边界构出顶点,这样必定是连通图,所以初始时点数和边数都是4。
由于题目说了不会有三条线交于一点的情况,所以处理起来方便很多。
细节:
1.k*x1+b之后的数可能会很大,但是直线最多只有30000条,所以离散化处理。
2.我两个地方写逗了卡了较长时间。写下来防止以后犯逗:
(1)建树顺手写在了输入直线数之后,简单的样例发现不了,因为直线右端点如果没重合的话是不会有影响的。应当写在离散化之后。范围就到离散化后最大的数。
(2)调用query2函数是L、R的取值问题,我为了图省事R直接取到xx+1,如果前一组样例离散化后值较大,后一组较小,则xx+1就会取到上一组的情况,导致答案出错,我尝试建树时范围更大些,但是结果还是WA。解决方案是不要图省事,就取到xx。或者每组样例memset一下sum数组,但是这样显然很慢。
#include<cstring>
#include<string>
#include<sstream>
#include<fstream>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<queue>
#include<deque>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<ctime>
#include<cstdlib>
#include<functional>
#include<cmath>
using namespace std;
#define PI acos(-1.0)
#define MAXN 30010
#define eps 1e-7
#define INF 0x3F3F3F3F //0x7FFFFFFF
#define LLINF 0x7FFFFFFFFFFFFFFF
#define seed 1313131
#define MOD 1000000007
#define ll long long
#define ull unsigned ll
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct node{
int y1, y2;
}line[MAXN];
int sum[MAXN << 2];
int n;
map<int, int> mp;
bool cmp(node x, node y){
return x.y1 < y.y1;
}
bool operator == (node p1, node p2){
return p1.y1 == p2.y1 && p1.y2 == p2.y2;
}
void pushup(int rt){
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void build(int l, int r, int rt){
sum[rt] = 0;
if(l == r) return ;
int m = (l + r) >> 1;
build(lson);
build(rson);
}
void update(int pos, int l, int r, int rt){
if(l == r){
sum[rt]++;
return ;
}
int m = (l + r) >> 1;
if(pos <= m) update(pos, lson);
else update(pos, rson);
pushup(rt);
}
int query1(int pos, int l, int r, int rt){
if(l == r){
return sum[rt];
}
int m = (l + r) >> 1;
if(pos <= m) query1(pos, lson);
else query1(pos, rson);
}
int query2(int L, int R, int l, int r, int rt){
if(L <= l && r <= R){
return sum[rt];
}
int m = (l + r) >> 1;
int res = 0;
if(L <= m) res += query2(L, R, lson);
if(R > m) res += query2(L, R, rson);
return res;
}
int aaa[MAXN];
int fuck[MAXN];
int main(){
int x1, x2;
int i, j, k, b;
ll ppp, lll;
while(scanf("%d%d", &x1, &x2) != EOF){
mp.clear();
ppp = 4;
lll = 4;
scanf("%d", &n);
int m = 0;
int flag = 0;
for(i = 0; i < n; i++){
scanf("%d%d", &k, &b);
line[m].y1 = k * x1 + b;
line[m].y2 = k * x2 + b;
if(line[m].y1 != line[m].y2) flag = 1;
aaa[m] = line[m].y2;
m++;
}
sort(aaa, aaa + m);
int xx = unique(aaa, aaa + m) - aaa;
if(x1 == x2 && flag == 0){
printf("%d\n", xx + 1);
continue;
}
for(i = 0; i < xx; i++){
mp[aaa[i]] = i + 1;
}
build(1, xx + 1, 1);
sort(line, line + m, cmp);
m = unique(line, line + m) - line;
// cout<<m<<endl;
ll ans = 0;
for(i = 0; i < m; i++){
j = i;
while(line[i + 1].y1 == line[i].y1 && i + 1 < m) i++;
ppp++;
lll += i - j + 1;
lll++;
for(int ii = j; ii <= i; ii++){
int ttt = mp[line[ii].y2];
int temp = query1(ttt, 1, n, 1);
if(!temp){
lll++;
ppp++;
}
if(ttt + 1 <= xx){
temp = query2(ttt + 1, xx, 1, n, 1);
ppp += temp;
lll += temp * 2;
}
}
for(int ii = j; ii <= i; ii++){
update(mp[line[ii].y2], 1, n, 1);
}
}
// cout<<ppp<<" "<<lll<<endl;
ans = 2 - ppp + lll - 1;
printf("%lld\n", ans);
}
return 0;
}