链接: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; }
ZOJ--3574--Under Attack II【线段树+欧拉公式】
原文地址:http://blog.csdn.net/zzzz40/article/details/41328693