NOI模拟(5.14) JSOID1T3 绝地反击 (bzoj5316)

绝地反击

题目背景:

5.14 模拟 JSOI2018D1T3

分析:计算几何 + 网络流 + 二分 + 网络流退流

 

又是我不会的新姿势,首先我们考虑如何暴力,对于最优的时间,我们可以肯定,一定有一个飞船是恰好在这个时间到了对应坐标,而没有任何停留,否则一定可以更优,那么我们二分最大时间,然后枚举每一条飞船作为最晚到达的那一个,然后就可以确定对应的位置,然后跑一遍二分图匹配即可,这样做的话普通写大概有50 ~ 60,加上一些特判和一些剪枝大概能够有90左右,当然,这个结果是loj得到的。

时间复杂度:O(n7/2logn~ n4logn),据说用HK可以卡过去?

 

Source:

 

/*
    created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <bitset>
 
inline char read() {
    static const int IN_LEN = 1024 * 1024;
    static char buf[IN_LEN], *s, *t;
    if (s == t) {
        t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
        if (s == t) return -1;
    }
    return *s++;
}
 
///*
template<class T>
inline void R(T &x) {
    static char c;
    static bool iosig;
    for (c = read(), iosig = false; !isdigit(c); c = read()) {
        if (c == -1) return ;
        if (c == '-') iosig = true; 
    }
    for (x = 0; isdigit(c); c = read()) 
        x = ((x << 2) + x << 1) + (c ^ '0');
    if (iosig) x = -x;
}
//*/

const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN];
char *oh = obuf;
inline void write_char(char c) {
	if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
	*oh++ = c;
}


template<class T>
inline void W(T x) {
	static int buf[30], cnt;
	if (x == 0) write_char('0');
	else {
		if (x < 0) write_char('-'), x = -x;
		for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
		while (cnt) write_char(buf[cnt--]);
	}
}

inline void flush() {
    for (int i = 0; i <= 10; ++i) std::cout << obuf[i];
    std::cout << '\n';
	fwrite(obuf, 1, oh - obuf, stdout), oh = obuf;
}
 
/*
template<class T>
inline void R(T &x) {
    static char c;
    static bool iosig;
    for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
        if (c == '-') iosig = true; 
    for (x = 0; isdigit(c); c = getchar()) 
        x = ((x << 2) + x << 1) + (c ^ '0');
    if (iosig) x = -x;
}
//*/

const int MAXN = 400 + 10;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int INF = MAXN;

struct node {
    int to, w, rev;
    node() {}
    node(int to, int w, int rev) : to(to), w(w), rev(rev) {}
} ;

std::vector<node> edge[MAXN << 1];
int dis[MAXN], temp[MAXN];
inline void add_edge(int x, int y, int w) {
    edge[x].push_back(node(y, w, edge[y].size()));
    edge[y].push_back(node(x, 0, edge[x].size() - 1));
}

inline bool bfs(int s, int t) {
    static std::queue<int> q;
    memset(dis, -1, sizeof(int) * (t + 5));
    memset(temp, 0, sizeof(int) * (t + 5));
    dis[s] = 0, q.push(s);
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        for (int p = 0; p < edge[cur].size(); ++p) {
            node *e = &edge[cur][p];
            if (e->w && dis[e->to] == -1) 
                dis[e->to] = dis[cur] + 1, q.push(e->to);
        }
    }
    return dis[t] != -1;
}

inline int dfs(int cur, int low, int t) {
    if (cur == t) return low;
    int delta = 0;
    for (int &p = temp[cur]; p < edge[cur].size(); ++p) {
        node *e = &edge[cur][p];
        if (e->w && dis[e->to] == dis[cur] + 1) {
            int ret = dfs(e->to, std::min(low - delta, e->w), t);
            e->w -= ret, edge[e->to][e->rev].w += ret;
            if ((delta += ret) == low) break ;
        }
    }
    return delta;
}

inline int dinic(int s, int t) {
    int ans = 0, ret = 0;
    while (bfs(s, t)) do ret = dfs(s, INF, t); while (ans += ret, ret);
    return ans;
}

struct point {
    double x, y;

    point() {}
    point(double x, double y) : x(x), y(y) {}

    inline point operator + (const point &a) const {
        return point(x + a.x, y + a.y);
    }

    inline point operator - (const point &a) const {
        return point(x - a.x, y - a.y);
    }

    inline point operator / (const double a) const {
        return point(x / a, y / a);
    }

    inline point operator * (const double a) const {
        return point(x * a, y * a);
    }

    inline double dis() {
        return sqrt(x * x + y * y);
    }

    inline double dis2() {
        return x * x + y * y;
    }

    inline point rotate(double ang) {
        double cos_a = cos(ang), sin_a = sin(ang);
        return point(x * cos_a - y * sin_a, x * sin_a + y * cos_a);
    }
} p[MAXN], st[MAXN];

int n, r, x, y;

inline point get_pos(point p, double dis, double flag) {
    double a = dis, b = r, c = p.dis();
    double cos_a = (b * b + c * c - a * a) / 2.00 / b / c;
    point cur = p / c * r;
    return cur = cur.rotate(flag * acos(cos_a)), cur;
}

inline bool check(point st, int id, double max) {
    int s = 0, t = 2 * n + 1;
    for (int i = s; i <= t; ++i) edge[i].clear();
    for (int i = 1; i < n; ++i) {
        st = st.rotate(2.0 * PI / n);
        int cc = 0;
        for (int j = 1; j <= n; ++j) 
            if (j != id && (p[j] - st).dis2() <= max * max) 
                add_edge(j, i + n, 1), cc++;
        if (cc == 0) return false;
    }
    for (int i = 1; i <= n; ++i) if (i != id) add_edge(s, i, 1);
    for (int i = 1; i < n; ++i) add_edge(i + n, t, 1);
    return (dinic(s, t) == n - 1);
}

inline bool check(double x) {
    for (int i = 1; i <= n; ++i) {
        if (x < p[i].dis() - r || x < r - p[i].dis()) return false;
        if (x > p[i].dis() + r) {
            st[i] = point(INF, INF);
            continue ;
        }
        st[i] = get_pos(p[i], x, 1);
    }
    for (int i = 1; i <= n; ++i)
        if (st[i].x != INF && check(st[i], i, x)) 
            return true;
    return false;
}

inline void solve() {
    R(n), R(r);
    for (int i = 1; i <= n; ++i) R(x), R(y), p[i] = point(x, y);
    std::random_shuffle(p + 1, p + n + 1);
    double l = -eps, r = 0;
    for (int i = 1; i <= n; ++i) r = std::max(p[i].dis() + ::r, r);
    int cnt = 0;
    while (l + eps < r) {
        double mid = (l + r) / 2;
        check(mid) ? r = mid : l = mid;
    }
    printf("%0.8f", r);
}

int main() {
    // freopen("in.in", "r", stdin);
    // freopen("fleet.out", "w", stdout);
    solve();
    return 0;
}

考虑如何优化复杂度,我们发现如果把x轴正半轴上的(r, 0)当做其中一个坐标,那么所有的可行方案与这个方案的角度都不超过2pi / n,那么我们考虑维护逆时针进行偏转的过程,我们发现,如果一开始先将(r, 0)定位为1号点,那么随着偏转,每一个点原有的可行匹配点会在某一时刻减少一个,也会在某一时刻增加一个,那么我们可以将每一种操作的偏转角度记录下来,然后将操作排序,显然操作总个数是O(n)的,每一次操作相当于从网络流建图中删掉一条边,或者增加一条边,我们只需要将这个流量退掉,或者加上就可以了,因为是二分图,所以一次只会影响三条边,还是很轻松的,因为流量改变只有一,所以一次退流或者加边的复杂度都应该是O(n + m)的,那么总复杂度都是O(n3logn)

 

Source:

/*
	created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <deque>
#include <iterator>
#include <map>

inline char read() {
	static const int IN_LEN = 1024 * 1024;
	static char buf[IN_LEN], *s, *t;
	if (s == t) {
		t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
		if (s == t) return -1;
	}
	return *s++;
}

// /*
template<class T>
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = read(), iosig = false; !isdigit(c); c = read()) {
		if (c == -1) return ;
		if (c == '-') iosig = true;	
	}
	for (x = 0; isdigit(c); c = read()) 
		x = ((x << 2) + x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
//*/

const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {
	if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
	*oh++ = c;
}

template<class T>
inline void W(T x) {
	static int buf[30], cnt;
	if (x == 0) write_char('0');
	else {
		if (x < 0) write_char('-'), x = -x;
		for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
		while (cnt) write_char(buf[cnt--]);
	}
}

inline void flush() {
	fwrite(obuf, 1, oh - obuf, stdout);
}

/*
template<class T>
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = getchar(), iosig = false; !isdigit(c); c = getchar()) {
		if (c == -1) return ;
		if (c == '-') iosig = true;	
	}
	for (x = 0; isdigit(c); c = getchar()) 
		x = ((x << 2) + x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
// */

const int MAXN = 200000 + 10;
const double PI = acos(-1.0);
const double eps = 1e-9;
const int INF = MAXN;

double block;
int s, t, n, r, x, y;
int dis[MAXN], temp[MAXN];
bool vis[MAXN];

struct point {
    double x, y;

    point() {}
    point(double x, double y) : x(x), y(y) {}

    inline double dis() {
        return sqrt(x * x + y * y);
    }

    inline double dis2() {
        return x * x + y * y;
    }
} p[MAXN];

struct node {
    int to, w, rev;
    node() {}
    node(int to, int w, int rev) : to(to), w(w), rev(rev) {}
} ;

std::vector<node> edge[MAXN];

struct data {
    double num;
    int u, v, type;
    data() {}
    data(double num, int u, int v, int type)
         : num(num), u(u), v(v), type(type) {}
    inline bool operator < (const data &a) const {
        return (num == a.num) ? (type > a.type) : (num < a.num);
    }
} q[MAXN << 1 | 1];

inline void add_edge(int x, int y, int w) {
    edge[x].push_back(node(y, w, edge[y].size()));
    edge[y].push_back(node(x, 0, edge[x].size() - 1));
}

inline bool bfs(int s, int t) {
    static std::queue<int> q;
    for (int i = s; i <= t; ++i) dis[i] = -1;
    for (int i = s; i <= t; ++i) temp[i] = 0;
    dis[s] = 0, q.push(s);
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        for (int p = 0; p < edge[cur].size(); ++p) {
            node *e = &edge[cur][p];
            if (e->w > 0 && dis[e->to] == -1) 
                dis[e->to] = dis[cur] + 1, q.push(e->to);
        }
    }
    return dis[t] != -1;
}

inline int dfs(int cur, int low, int t) {
    if (cur == t) return low;
    int delta = 0;
    for (int &p = temp[cur]; p < edge[cur].size(); ++p) {
        node *e = &edge[cur][p];
        if (dis[e->to] == dis[cur] + 1 && e->w > 0) {
            int ret = dfs(e->to, std::min(low - delta, e->w), t);
            e->w -= ret, edge[e->to][e->rev].w += ret;
            if ((delta += ret) == low) break ;
        }
    }
    return delta;
}

int ret, ans;

inline void pop_flow(int u, int v) {
    std::vector<node>::iterator it, it1;
    bool flag;
    for (it = edge[u].begin(); it != edge[u].end(); ++it) 
        if (it->to == v) {
            flag = it->w, it = edge[u].erase(it);
            break ;
        } 
    for (; it != edge[u].end(); ++it) edge[it->to][it->rev].rev--;
    for (it = edge[v].begin(); it != edge[v].end(); ++it)
        if (it->to == u) {
            it = edge[v].erase(it);
            break ;
        }
    for (; it != edge[v].end(); ++it) edge[it->to][it->rev].rev--;
    if (flag) return ;
    ans--;
    for (it = edge[s].begin(); it != edge[s].end(); ++it)
        if (it->to == u) {
            it->w ^= 1, edge[it->to][it->rev].w ^= 1;
            break ;
        }
    for (it = edge[t].begin(); it != edge[t].end(); ++it)
        if (it->to == v) {
            it->w ^= 1, edge[it->to][it->rev].w ^= 1;
            break ;
        }
    while (bfs(s, t))
        do ret = dfs(s, INF, t);
        while (ans += ret, ret);
}

inline bool check(double x) {
    int cnt = 0;
    for (int i = s; i <= t; ++i) edge[i].clear();
    for (int i = 1; i <= n; ++i) {
        double dis = p[i].dis();
        if (dis > x + r || dis < r - x) return false;
        if (dis + r <= x) for (int j = 1; j <= n; ++j) add_edge(i, j + n, 1);
        else {
            double angle1 = atan2(p[i].y, p[i].x);
            double angle2 = acos(((double)r * r + dis * dis - x * x) 
                / 2.0 / dis / r);
            double al = angle1 - angle2, ar = angle1 + angle2;
            while (al < 0) al += 2.0 * PI;
            while (ar < 0) ar += 2.0 * PI;
            int pl = al / block, pr = ar / block;
            q[++cnt] = data(al - block * pl, i, pl + 1 + n, 1), pl++;
            q[++cnt] = data(ar - block * pr, i, pr + 1 + n, 0), pr++;
            if (al <= ar) for (int j = pl + 1; j <= pr; ++j) 
                add_edge(i, j + n, 1);
            else {
                for (int j = pl + 1; j <= n; ++j) add_edge(i, j + n, 1);
                for (int j = 1; j <= pr; ++j) add_edge(i, j + n, 1);
            }
        } 
    }
    for (int i = 1; i <= n; ++i) add_edge(s, i, 1), add_edge(i + n, t, 1);
    std::sort(q + 1, q + cnt + 1), ret = ans = 0;
    while (bfs(s, t))
        do ret = dfs(s, INF, t);
        while (ans += ret, ret);
    if (ans == n) return true;
    for (int i = 1; i <= cnt; ++i) {
        if (q[i].type) {
            add_edge(q[i].u, q[i].v, 1);
            while (bfs(s, t))
                do ret = dfs(s, INF, t);
                while (ans += ret, ret);
            if (ans == n) return true;
        } else pop_flow(q[i].u, q[i].v);
    }
    return false;
}

inline void solve() {
    R(n), R(r), block = PI * 2.00 / n, s = 0, t = 2 * n + 1;
    for (int i = 1; i <= n; ++i) R(x), R(y), p[i] = point(x, y);
    double l = -eps, r = 300;
    while (l + eps < r) {
        double mid = (l + r) / 2;
        (check(mid)) ? r = mid : l = mid;
    }
    printf("%0.8f", r);
}

int main() {
    freopen("in.in", "r", stdin);
    solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值