【LG2154】[SDOI2009]虔诚的墓主人

【LG2154】[SDOI2009]虔诚的墓主人

题面

洛谷

题解

如果您没有看懂题,请反复阅读题面及样例

可以发现,对于某一个点,它的答案就是上下左右几个组合数乘起来。
这样直接做复杂度显然爆炸,考虑怎么优化这个东西。

我们可以固定左右,在某两个左右之间维护上下有多少个,这样子的话左右的贡献就是不变的,而且你最多只会变化\(O(n)\)次左右边界,复杂度有保证。

这样的话,每次查询一个左右边界内上下的贡献,用线段树维护即可。(描述可能有些模糊,具体详见代码)

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring> 
#include <cmath> 
#include <algorithm>
#include <vector> 
using namespace std; 
inline int gi() {
    register int data = 0, w = 1;
    register char ch = 0;
    while (!isdigit(ch) && ch != '-') ch = getchar(); 
    if (ch == '-') w = -1, ch = getchar(); 
    while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); 
    return w * data; 
} 
const int MAX_N = 1e5 + 5;
#define lson (o << 1)
#define rson (o << 1 | 1) 
int sum[MAX_N << 2]; 
void modify(int o, int l, int r, int pos, int v) { 
    if (l == r) return (void)(sum[o] = v); 
    int mid = (l + r) >> 1; 
    if (pos <= mid) modify(lson, l, mid, pos, v); 
    else modify(rson, mid + 1, r, pos, v); 
    sum[o] = sum[lson] + sum[rson]; 
} 
int query(int o, int l, int r, int ql, int qr) { 
    if (ql <= l && r <= qr) return sum[o]; 
    int mid = (l + r) >> 1, res = 0; 
    if (ql <= mid) res += query(lson, l, mid, ql, qr); 
    if (qr > mid) res += query(rson, mid + 1, r, ql, qr); 
    return res; 
} 
int W, H, N, K; 
int x[MAX_N], y[MAX_N], ox[MAX_N], oy[MAX_N], tx, ty; 
vector<int> vec[MAX_N]; 
int C[MAX_N][15], c1[MAX_N], c2[MAX_N]; 
bool cmp(const int &l, const int &r) { return y[l] < y[r]; } 
int main () { 
#ifndef ONLINE_JUDGE 
    freopen("cpp.in", "r", stdin); 
#endif 
    W = gi(), H = gi(), N = gi(); 
    for (int i = 1; i <= N; i++) x[i] = ox[i] = gi(), y[i] = oy[i] = gi(); 
    K = gi(); 
    sort(&ox[1], &ox[N + 1]), sort(&oy[1], &oy[N + 1]); 
    tx = unique(&ox[1], &ox[N + 1]) - ox - 1; 
    ty = unique(&oy[1], &oy[N + 1]) - oy - 1; 
    for (int i = 1; i <= N; i++) { 
        x[i] = lower_bound(&ox[1], &ox[tx + 1], x[i]) - ox; 
        y[i] = lower_bound(&oy[1], &oy[ty + 1], y[i]) - oy; 
    } 
    for (int i = 1; i <= N; i++) vec[x[i]].push_back(i), c1[y[i]]++; 
    for (int i = 1; i <= tx; i++) vec[i].push_back(N + 1); 
    y[N + 1] = ty + 1; 
    for (int i = 1; i <= tx; i++) sort(vec[i].begin(), vec[i].end(), cmp); 
    for (int i = 0; i <= N; i++) C[i][0] = 1; 
    for (int i = 1; i <= N; i++) 
        for (int j = 1; j <= min(i, K); j++) 
            C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
    int ans = 0; 
    for (int i = 1; i <= tx; i++) { 
        int res = 0, sz = vec[i].size(); 
        for (int j = 0; j < sz; j++) { 
            int l = j ? y[vec[i][j - 1]] : 0, r = y[vec[i][j]]; 
            if (l + 1 <= r - 1) ans += res * query(1, 1, ty, l + 1, r - 1); 
            if (j == sz - 1) break; 
            res = C[j + 1][K] * C[sz - j - 2][K]; 
            ++c2[r]; 
            modify(1, 1, ty, r, C[c2[r]][K] * C[c1[r] - c2[r]][K]); 
        } 
    } 
    if (ans < 0) ans += 2147483648ll; 
    printf("%d\n", ans); 
    return 0; 
} 

猜你喜欢

转载自www.cnblogs.com/heyujun/p/11695266.html