NOIP 2017 Senior 6

来源:互联网 发布:淘宝推广收费标准 编辑:程序博客网 时间:2024/06/05 15:11

传送门(JZOJ)

1.对于 30% 的数据
直接模拟即可

2.对于 50% 的数据(50000,查询少)
发现时间够,但是空间不够,又发现询问次数比较少,因此可以只保存部分行和最后一列。模拟即可。

3.对于 70% 的数据(仅一行)
由于只有一行,我们可以每个人对应一个平衡树结点,然后想一个排序方法。方法就是维护一个优先级,新插入结点的优先级为 n + i,i 指第 i 次查询。套用名次树模板即可,期望时间复杂度 O(n * log n)。

4.对于 100% 的数据
以上做法都不用动脑,100% 的就要考虑下了。参考平衡树做法(让新插入的值的位置为n + i),我们可以考虑用动态开结点的线段树:为每一行的前 m - 1 个人分别开一棵(简称行线段树),再为最后一列单独开一棵(简称列线段树)。但是一开始的初始化怎么做?不可能给每个叶节点赋值吧。这时就要用到线段树的灵活性了。
如果我们难以对每个结点的 size 进行初始化,怎么办?改变 size 的含义!之前 size 表示在 r - l + 1 (这个我们是知道的)中,有多少个叶结点(或者叶结点的和为多少)。现在我们改成 r - l + 1 - 叶结点个数,不就不用初始化了吗?
每个人的编号也是不用初始化的,我们可以算。如果是新加入的人,我们可以保存他的编号。如果这个人的在线段树中的位置大于等于 m(以行线段树为例),我们就可以知道这个人是新加入的,这个人的编号我们是保存了的。如果小于 m,这个人一定不是新加入的,这就意味着它可以算出来!
至此,所有初始化问题已经解决,剩下的问题也就可以不动脑了。

参考代码

#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>#include <bitset>typedef long long INT;using std::cin;using std::cout;using std::endl;inline INT readIn(){    INT a = 0;    bool minus = false;    char ch = getchar();    while (!(ch == '-' || (ch >= '0' && ch <= '9'))) ch = getchar();    if (ch == '-')    {        minus = true;        ch = getchar();    }    while (ch >= '0' && ch <= '9')    {        a = a * 10 + (ch - '0');        ch = getchar();    }    if (minus) a = -a;    return a;}inline void printOut(INT x){    char buffer[20];    INT length = 0;    bool minus = x < 0;    if (minus) x = -x;    do    {        buffer[length++] = x % 10 + '0';        x /= 10;    } while (x);    if (minus) buffer[length++] = '-';    do    {        putchar(buffer[--length]);    } while (length);    putchar('\n');}const INT maxn = INT(3e5) + 5;INT n, m, q;class SegTree{    struct Node    {        INT size;        INT val;        Node* lc;        Node* rc;        Node() : size(), val(), lc(), rc() {}    };    static Node pool[6000000];    static Node* cnt;    Node* null;    Node* root;#define DEF INT mid = (l + r) >> 1#define CNT node, l, r#define LC node->lc, l, mid#define RC node->rc, mid + 1, r#define PARAM Node* &node, INT l, INT r    void alloc(Node* &r)    {        if (r == null)        {            r = new(cnt++) Node;            r->lc = r->rc = null;        }    }    INT findKth(PARAM, INT k, INT& pos)    {        alloc(node);        if (l == r)        {            node->size = 1;            pos = l;            return node->val;        }        DEF;        INT s = mid - l + 1;        s = s - node->lc->size;        INT ret;        if (k <= s) ret = findKth(LC, k, pos);        else ret = findKth(RC, k - s, pos);        node->size = node->lc->size + node->rc->size;        return ret;    }    INT g_Pos, g_Val;    void insert(PARAM)    {        alloc(node);        if (l == r)        {            node->size = 0;            node->val = g_Val;            return;        }        DEF;        if (g_Pos <= mid) insert(LC);        else insert(RC);        node->size = node->lc->size + node->rc->size;    }public:    SegTree()    {        null = new Node;        root = null->lc = null->rc = null;    }    INT kth(INT k, INT& pos)    {        return findKth(root, 1, std::max(n, m) + q, k, pos);    }    void insert(INT pos, INT val)    {        g_Pos = pos;        g_Val = val;        insert(root, 1, std::max(n, m) + q);    }} trees[maxn], colum;SegTree::Node SegTree::pool[6000000];SegTree::Node* SegTree::cnt = pool;INT size[maxn];void run(){    n = readIn();    m = readIn();    q = readIn();    for (int i = 1; i <= n; i++)        size[i] = m - 1;    size[n + 1] = n;    for (int i = 1; i <= q; i++)    {        INT x = readIn();        INT y = readIn();        if (y == m)        {            INT pos;            INT ans = colum.kth(x, pos);            if (pos <= n)                ans = m * pos;            colum.insert(++size[n + 1], ans);            printOut(ans);        }        else        {            INT pos1;            INT ans = trees[x].kth(y, pos1);            if (pos1 < m)                ans = m * (x - 1) + pos1;            INT pos2;            INT ins = colum.kth(x, pos2);            if (pos2 <= n)                ins = m * pos2;            trees[x].insert(++size[x], ins);            colum.insert(++size[n + 1], ans);            printOut(ans);        }    }}int main(){#ifndef LOCAL    freopen("phalanx.in", "r", stdin);    freopen("phalanx.out", "w", stdout);#endif    run();    return 0;}

其实我不建议用内存池,因为结点数不是特别好算(理论上这个可是要 2 * 20 * 300000 的)。考试时不可能卡你的 new,而用 new 就不会出现溢出错误,所以建议用 new 就可以了。(用 new 方法:alloc 中改成 r = new Node;