【NOI2018模拟3.10】子序列

Description:

【NOI2018模拟3.10】子序列_第1张图片

题解:

好把我竟然打了lj的题解方法。

代码长常数大。

大概就是用个优先队列来维护。

每次提出最小的。

考虑两种转移:
1.在它后面加个最小的。

2.把它的最后一个删掉,找个次小的。

第一种转移预处理。

可以用主席树来维护第二种转移。

比较大小的话就用trie上倍增。

Code:

#pragma G++ optimize (2)
#include
#include
#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define low(a) ((a) & -(a))
using namespace std;

const int C = 1e5;
const int N = 1e5 + 5, M = 1e7 + 5;

int n, k, seed, mo, a[N];

int t[M][2], w[M], tt;

struct tree {
    int pl, pr, rt; 
    void dg(int &i, int x, int y) {
        if(!i) i = ++ tt;
        if(x == y) {if(!w[i]) w[i] = pr; return;}
        int m = x + y >> 1;
        if(pl <= m) dg(t[i][0], x, m); else dg(t[i][1], m + 1, y);
        w[i] = w[t[i][0]] | w[t[i][1]];
    }
    int dd(int &i, int x, int y) {
        if(!w[i] || y < pl || x > pr) return 0;
        if(x == y) return w[i];
        int m = x + y >> 1,  p = dd(t[i][0], x, m);
        return p ? p : dd(t[i][1], m + 1, y);
    }
    int fi(int l, int r) {pl = l; pr = r; return dd(rt, 1, C);}
} tr[N];

int find(int x, int l, int r) {
    int ans = n + 1; x = n - x + 1;
    while(x) {
        int p = tr[x].fi(l, r);
        if(p && (a[p] < a[ans] || a[p] == a[ans] && p < ans)) ans = p;
        x -= low(x);
    }
    return ans;
}

int ss[N * 2];
int fa[17][N * 2];
map<int, int> son[N * 2]; int td = 1, dep[N * 2], z[N * 2];
int add(int x, int c) {
    if(son[x][c]) return son[x][c];
    son[x][c] = ++ td;
    dep[td] = dep[x] + 1; z[td] = c;
    fa[0][td] = x; fo(i, 1, 16) fa[i][td] = fa[i - 1][fa[i - 1][td]];
    ss[td] = ((ll) ss[x] * seed + c) % mo;
    return td;
}

struct node {
    int x, f, t;
    node(int X, int F, int T) {x = X, f = F, t = T;}
    node() {}
};

int log2[N];

bool operator < (node a, node b) {
    int t1 = a.t, t2 = b.t, p = max(log2[dep[t1]], log2[dep[t2]]);
    if(dep[t1] > dep[t2]) {
        fd(i, p, 0) if(dep[fa[i][t1]] >= dep[t2]) t1 = fa[i][t1];
    }
    else
    fd(i, p, 0) if(dep[fa[i][t2]] >= dep[t1]) t2 = fa[i][t2];
    if(t1 == t2) return dep[a.t] > dep[b.t];
    fd(i, p, 0) if(fa[i][t1] != fa[i][t2]) t1 = fa[i][t1], t2 = fa[i][t2];
    return z[t1] > z[t2];
} //remember have rev

priority_queue q;

int fi[N], nt[N], te[N];

void read(int &n)
{
    char ch=' ';
    for(; ch < '0' || ch > '9'; ch = getchar());
    for(;ch>='0' && ch<='9';ch=getchar())n=n*10+ch-48;
}

void write(int x) {
    if(x == 0) putchar('0');
    int d[10]; d[0] = 0;
    while(x) d[++ d[0]] = x % 10, x /= 10;
    for(; d[0]; d[0] --) putchar(d[d[0]] + '0');
    putchar('\n');
}

int main() {
    freopen("sequence.in", "r", stdin);
    freopen("sequence.out", "w", stdout);
    scanf("%d %d %d %d", &n, &k, &seed, &mo);
    fo(i, 1, n) read(a[i]);
    fo(i, 1, 16) log2[1 << i] = 1;
    fo(i, 1, n) log2[i] += log2[i - 1];
    a[n + 1] = 1e9;
    fd(i, n, 1) {
        nt[i] = fi[a[i]];
        fi[a[i]] = i;
    }
    {
        int xx = n + 1;
        fd(i, n, 0) {
            te[i] = xx;
            if(a[i] <= a[xx]) xx = i;
        }
    }
    fo(i, 1, n) {
        int x = n - i + 1;
        while(x <= n) {
            tr[x].pl = a[i]; tr[x].pr = i;
            tr[x].dg(tr[x].rt, 1, C);
            x += low(x);
        }
    }
//  return 0;
    q.push(node(0, 0, 1));
    fo(ii, 1, k + 1) {
        node c = q.top(); q.pop(); 
        if(c.x) write(ss[c.t]);
        int x = te[c.x];
        if(x <= n) q.push(node(x, c.x, add(c.t, a[x])));
        if(!c.x) continue;
        x = nt[c.x];
        if(!x) x = find(c.f + 1, a[c.x] + 1, C);
        if(x <= n) q.push(node(x, c.f, add(fa[0][c.t], a[x])));
    }
}

你可能感兴趣的:(线段树,Trie,分治)