洛谷P3120 [USACO15FEB]牛跳房子(动态开节点线段树)

题意

题目链接

Sol

(f[i][j])表示前(i)(j)列的贡献,转移的时候枚举从哪里转移而来,复杂度(O(n^4))

然后考虑每一行的贡献,动态开节点线段树维护一下每种颜色的答案

转移的时候用总的方案减去相同颜色的方案

复杂度(O(n^2 log^2 n))

#include<bits/stdc++.h> 
#define LL long long 
using namespace std;
const int MAXN = 1001, INF = 1e9 + 10, mod = 1000000007, SS = MAXN * MAXN * 10;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int add(int x, int y) {
    if(x + y < 0) return x + y + mod;
    return x + y >= mod ? x + y - mod : x + y;
}
int N, M, K, c[MAXN][MAXN], f[MAXN][MAXN], tot;
int root[SS], ls[SS], rs[SS], sum[SS];
void IntAdd(int &k, int l, int r, int pos, int val) {
    if(!k) k = ++tot;
    sum[k] = add(sum[k], val);
    if(l == r) return ;
    int mid = l + r >> 1;
    if(pos <= mid) IntAdd(ls[k], l, mid, pos, val);
    else IntAdd(rs[k], mid + 1, r, pos, val);
}
int Query(int k, int l, int r, int ll, int rr) {
    if(!k) return 0;
    if(ll <= l && r <= rr) return sum[k];
    int mid = l + r >> 1;
    if(ll > mid) return Query(rs[k], mid + 1, r, ll, rr);
    else if(rr <= mid) return Query(ls[k], l, mid, ll, rr);
    else return add(Query(ls[k], l, mid, ll, rr), Query(rs[k], mid + 1, r, ll, rr)); 
}
signed main() {
    N = read(); M = read(); K = read();
    f[1][1] = 1;
    for(int i = 1; i <= N; i++) {
        for(int j = 1; j <= M; j++) {
            int k = read(); c[i][j] = k;
            if(!f[i][j]) f[i][j] = add(Query(root[0], 1, M, 1, j - 1), -Query(root[k], 1, M, 1, j - 1));
            
        }
        for(int j = 1; j <= M; j++)
            IntAdd(root[0], 1, M, j, f[i][j]),
            IntAdd(root[c[i][j]], 1, M, j, f[i][j]);
    }
    cout << f[N][M];
    return 0;
}
原文地址:https://www.cnblogs.com/zwfymqz/p/10216215.html