ZOJ--3574--Under Attack II【线段树+欧拉公式】

链接: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值