JZOJ ???? 哟

没有传送门(真的没有)
题目大意

m 条从 ( 0 , 0 ) 发射出去的射线,用 ( x , y ) 表示上面的一点以代表这条射线。有 n 个人坐标为 ( x i , y i ) ,它们会走到极角离自己最近的射线上(垃圾出题人不说极角二字,只有用灵性的选手才能领会到出题人的意图)。如果有两条射线,那么这个人会任选一条走(显然,一个人要么有一条备选射线,要么有两条备选射线,没有别的情况)。

每条射线有个最大负荷,最多只能有 c i 个人选择走向它。这意味着可能有人不会走向任何一条射线。希望你求出最多能有多少个人走向了射线,并要求输出具体方案。

考场上的思路

题意都是假的做个铲铲??

思路

显然我们要先预处理出每个人的备选射线。我的方法是把所有点(射线和人)按极角排序,然后逆时针扫一遍,再顺时针扫一遍。为了正确处理边上的情况,我干脆把所有点都复制了一遍放在数组尾部,这样无论如何都不会漏了。

有了备选射线,我们就可以很容易想到二分图匹配。可以使用时间复杂度为 O ( n m ) 的 Dinic 算法,不过这显然会被卡,最多只能得到 60 分。有没有更好的方法呢?

这么大的数据,显然不会叫你用网络流,可以考虑贪心。注意到这个平面直角坐标系的形态大致如下:

也就是说点被夹在两条射线中间。首先,如果一个点只有一条备选射线,我们就先选它们,现在剩下的点都有两条备选射线(图显然不标准,但是你假设它们的两侧都是它们的备选射线)。考虑如果有两条射线中间没有点(如图),那么这个环就退化成一条链。我们可以贪心:让点尽量选择自己顺时针方向的射线,如果不行,再选择逆时针方向的射线。

那环上的问题该怎么解决呢?我们考虑某两条射线中间的点。我们强制让某些点选择顺时针方向的射线,再让强制剩下的点不准选顺时针方向的射线,然后把后面的当作链,问题就解决了。

我们选择中间点数最少的两条射线作为枚举对象。设枚举次数为 t ,那么最多有 n t 条射线。由于每次贪心的时间复杂度为 O ( m ) ,因此这个部分的总时间复杂度为 O ( n ) ,能够得到 100 分。

参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef int INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0;
    bool positive = true;
    char ch = getchar();
    while (!(std::isdigit(ch) || ch == '-')) ch = getchar();
    if (ch == '-')
    {
        positive = false;
        ch = getchar();
    }
    while (std::isdigit(ch))
    {
        (a *= 10) -= ch - '0';
        ch = getchar();
    }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20];
    int length = 0;
    if (x < 0) putchar('-');
    else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
}

struct Vector
{
    double x, y;
    Vector() {}
    Vector(const double& x, const double& y) : x(x), y(y) {}
    void read()
    {
        x = readIn() * 10;
        y = readIn() * 10;
    }
};
typedef Vector Point;
int dcmp(const double& x)
{
    const double EPS = 1e-7;
    if (std::abs(x) <= EPS)
        return 0;
    return x < 0 ? -1 : 1;
}
Vector operator-(const Vector& a, const Vector& b)
{
    return Vector(a.x - b.x, a.y - b.y);
}
double Dot(const Vector& a, const Vector& b)
{
    return a.x * b.x + a.y * b.y;
}
double Length(const Vector& v)
{
    return std::sqrt(Dot(v, v));
}
double Cross(const Vector& a, const Vector& b)
{
    return a.x * b.y - a.y * b.x;
}

const int INF = (~(int(1) << (sizeof(int) * 8 - 1))) >> 1;
const int maxn = int(2e5) + 5;
int n, m;
Point points[maxn];
struct Station
{
    Vector vec;
    int c;
    void read()
    {
        vec.read();
        c = readIn();
    }
} stations[maxn];
struct Object
{
    int type;
    int idx;
    Vector vec;
    Object() {}
    Object(const Station& s, int idx) : type(1), idx(idx), vec(s.vec) {}
    Object(const Point& p, int idx) : type(2), idx(idx), vec(p) {}
    bool operator<(const Object& b) const
    {
        return dcmp(std::atan2(vec.y, vec.x) - std::atan2(b.vec.y, b.vec.x)) < 0;
    }
};
std::vector<Object> objects;
int neighbor[maxn][2];

#define RunInstance(x) delete new x
struct brute
{
    struct NetworkFlow
    {
        struct Edge
        {
            int from, to, cap, flow;
            Edge() {}
            Edge(int from, int to, int cap) : flow(), from(from), to(to), cap(cap) {}
        };
        std::vector<Edge> edges;
        std::vector<std::vector<int> > G;
        int s, t;
        int addEdge(int from, int to, int cap)
        {
            edges.push_back(Edge(from, to, cap));
            edges.push_back(Edge(to, from, 0));
            int i = edges.size();
            G[from].push_back(i - 2);
            G[to].push_back(i - 1);
            return i - 2;
        }

        int level[maxn * 3];
        std::vector<int> cur;
        int Dinic(int node, int opt)
        {
            if (node == t || !opt)
                return opt;
            int flow = 0;
            for (int& i = cur[node]; i < G[node].size(); i++)
            {
                Edge& e = edges[G[node][i]];
                int t;
                if (e.flow < e.cap && level[node] + 1 == level[e.to] && (t = Dinic(e.to, std::min(opt, e.cap - e.flow))))
                {
                    flow += t;
                    opt -= t;
                    e.flow += t;
                    edges[G[node][i] ^ 1].flow -= t;
                    if (!opt) break;
                }
            }
            return flow;
        }

        struct Queue
        {
            int c[maxn * 3];
            int head, tail;
            Queue() : head(), tail() {}
            void clear() { head = tail = 0; }
            bool empty() { return head == tail; }
            void push(int x) { c[tail++] = x; }
            void pop() { head++; }
            int front() { return c[head]; }
        } q;
        int stamp;
        int vis[maxn * 3];
        bool BFS()
        {
            q.clear();
            level[s] = 0;
            stamp++;
            vis[s] = stamp;
            q.push(s);
            while (!q.empty())
            {
                int from = q.front();
                q.pop();
                for (int i = 0; i < G[from].size(); i++)
                {
                    const Edge& e = edges[G[from][i]];
                    if (e.flow < e.cap && vis[e.to] != stamp)
                    {
                        level[e.to] = level[e.from] + 1;
                        vis[e.to] = stamp;
                        q.push(e.to);
                    }
                }
            }
            return vis[t] == stamp;
        }

        int maxFlow()
        {
            stamp = 0;
            std::memset(vis, 0, sizeof(vis));
            int flow = 0;
            while (BFS())
            {
                cur.clear();
                cur.resize(G.size());
                flow += Dinic(s, INF);
            }
            return flow;
        }
    } nf;

    int edge[maxn][2];

    brute()
    {
        nf.G.resize(n + m + 2);
        nf.s = n + m;
        nf.t = n + m + 1;
        for (int i = 0; i < n; i++)
        {
            edge[i][0] = nf.addEdge(i, n + neighbor[i][0], 1);
            if (~neighbor[i][1])
                edge[i][1] = nf.addEdge(i, n + neighbor[i][1], 1);
            else
                edge[i][1] = -1;
        }
        for (int i = 0; i < n; i++)
            nf.addEdge(nf.s, i, 1);
        for (int i = 0; i < m; i++)
            nf.addEdge(n + i, nf.t, stations[i].c);
        printOut(nf.maxFlow());
        putchar('\n');
        for (int i = 0; i < n; i++)
        {
            if (nf.edges[edge[i][0]].flow)
            {
                printOut(i);
                putchar(' ');
                printOut(nf.edges[edge[i][0]].to - n);
                putchar('\n');
            }
            else if (~edge[i][1] && nf.edges[edge[i][1]].flow)
            {
                printOut(i);
                putchar(' ');
                printOut(nf.edges[edge[i][1]].to - n);
                putchar('\n');
            }
        }
    }
};
struct work
{
    std::vector<std::pair<int, int> > ans;

    int idx[maxn];
    int count[maxn];
    struct NewObject
    {
        int type;
        int idx;
        int next;
        NewObject() {}
        NewObject(int type, int idx) : type(type), idx(idx) {}
        NewObject(const Object& obj) : type(obj.type), idx(obj.idx) {}
    };
    std::vector<NewObject> nobjects;

    int remain[maxn];
    int GetVal(int pos, int val)
    {
        for (int i = 0; i < m; i++)
            remain[i] = stations[i].c;
        int ret = 0;
        int pre = -1;
        for (int i = 1; i < nobjects.size(); i++)
        {
            if (nobjects[i].type == 1)
            {
                pre = nobjects[i].idx;
            }
            else
            {
                if (!~pre && val && remain[pos])
                {
                    val--;
                    ret++;
                    remain[pos]--;
                }
                else
                {
                    if (~pre && remain[pre])
                    {
                        ret++;
                        remain[pre]--;
                    }
                    else if (remain[nobjects[i].next])
                    {
                        ret++;
                        remain[nobjects[i].next]--;
                    }
                    else
                    {
                        int a = 0;
                    }
                }
            }
        }
        return ret;
    }
    void MakeAns(int pos, int val)
    {
        for (int i = 0; i < m; i++)
            remain[i] = stations[i].c;
        int ret = 0;
        int pre = -1;
        for (int i = 1; i < nobjects.size(); i++)
        {
            if (nobjects[i].type == 1)
            {
                pre = nobjects[i].idx;
            }
            else
            {
                if (!~pre && val && remain[pos])
                {
                    val--;
                    ans.push_back(std::make_pair(nobjects[i].idx, pos));
                    remain[pos]--;
                }
                else
                {
                    if (~pre && remain[pre])
                    {
                        ans.push_back(std::make_pair(nobjects[i].idx, pre));
                        remain[pre]--;
                    }
                    else if (remain[nobjects[i].next])
                    {
                        ans.push_back(std::make_pair(nobjects[i].idx, nobjects[i].next));
                        remain[nobjects[i].next]--;
                    }
                }
            }
        }
    }

    work() : count()
    {
        for (int i = 0; i < n; i++)
            if (!~neighbor[i][1])
                if (int& t = stations[neighbor[i][0]].c)
                {
                    t--;
                    ans.push_back(std::make_pair(i, neighbor[i][0]));
                }

        objects.resize(objects.size() >> 1);
        // assert(m >= 2);
        int cnt = -1;
        for (int i = 0; i < objects.size(); i++)
        {
            const Object& obj = objects[i];
            if (obj.type == 1)
            {
                idx[obj.idx] = i;
                cnt = obj.idx;
            }
            else if (obj.type == 2)
            {
                if (!~neighbor[obj.idx][1])
                    continue;
                if (~cnt)
                    count[cnt]++;
            }
        }
        for (int i = 0; i < objects.size() && objects[i].type != 1; i++)
            count[cnt]++;

        int minPos = std::min_element(count, count + m) - count;
        {
            int pos = 0;
            for (int i = 0; i < objects.size(); i++)
                if (objects[i].type == 1 && objects[i].idx == minPos)
                {
                    pos = i;
                    break;
                }
            for (int i = pos; i < objects.size(); i++)
                if (!(objects[i].type == 2 && !~neighbor[objects[i].idx][1]))
                    nobjects.push_back(objects[i]);
            for (int i = 0; i < pos; i++)
                if (!(objects[i].type == 2 && !~neighbor[objects[i].idx][1]))
                    nobjects.push_back(objects[i]);
            int next = nobjects[0].idx;
            for (int i = nobjects.size() - 1; ~i; i--)
                if (nobjects[i].type == 1)
                    next = nobjects[i].idx;
                else if (nobjects[i].type == 2)
                    nobjects[i].next = next;
        }

        int maxVal = 0;
        int maxIdx = 0;
        for (int i = 0; i <= count[minPos]; i++)
        {
            int t;
            if ((t = GetVal(minPos, i)) > maxVal)
            {
                maxVal = t;
                maxIdx = i;
            }
        }
        MakeAns(minPos, maxIdx);

        printOut(ans.size());
        putchar('\n');
        for (int i = 0; i < ans.size(); i++)
        {
            printOut(ans[i].first);
            putchar(' ');
            printOut(ans[i].second);
            putchar('\n');
        }
    }
};

void run()
{
    n = readIn();
    m = readIn();
    for (int i = 0; i < n; i++)
        points[i].read();
    for (int i = 0; i < m; i++)
        stations[i].read();

    for (int i = 0; i < n; i++)
        objects.push_back(Object(points[i], i));
    for (int i = 0; i < m; i++)
        objects.push_back(Object(stations[i], i));
    std::sort(objects.begin(), objects.end());
    for (int i = 0, to = objects.size(); i < to; i++)
        objects.push_back(objects[i]);

    int pre;
    pre = -1;
    for (int i = 0; i < objects.size(); i++)
    {
        const Object& obj = objects[i];
        if (obj.type == 1)
            pre = obj.idx;
        else if (obj.type == 2)
            if (~pre)
                neighbor[obj.idx][0] = pre;
    }
    pre = -1;
    for (int i = objects.size() - 1; ~i; i--)
    {
        const Object& obj = objects[i];
        if (obj.type == 1)
            pre = obj.idx;
        else if (obj.type == 2)
            if (~pre)
                neighbor[obj.idx][1] = pre;
    }

    for (int i = 0; i < n; i++)
    {
        if (neighbor[i][0] == neighbor[i][1])
            neighbor[i][1] = -1;
        else
        {
            const Vector& vec1 = stations[neighbor[i][0]].vec;
            const Vector& vec2 = stations[neighbor[i][1]].vec;
            double cos1 = Dot(points[i], vec1) * Length(vec2);
            double cos2 = Dot(points[i], vec2) * Length(vec1);
            if (dcmp(cos1 - cos2) < 0)
            {
                neighbor[i][0] = neighbor[i][1];
                neighbor[i][1] = -1;
            }
            else if (dcmp(cos1 - cos2) > 0)
                neighbor[i][1] = -1;
        }
    }

    if (m == 1)
        RunInstance(brute);
    else
        RunInstance(work);
}

int main()
{
#ifndef LOCAL
    freopen("yo.in", "r", stdin);
    freopen("yo.out", "w", stdout);
#endif
    run();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lycheng1215/article/details/80903830