In mathematics you don't understand things, you just get used to them.

标签 data structures 下的文章

  link.

  我发现 Hydro OJ 意外地好用,比 darkbzoj 高明.

  考虑点积的几何意义:投影.可以发现答案就是用一根与询问向量正交的直线从沿询问向量方向无限远处向原点迫近,碰到的第一个向量就是答案.然后你会发现,如果把正交直线的方程写出来,然后解一下与集合内向量相碰撞的方程,就会得到一个结论,即询问的答案是 $\max {y \times (\frac{x}{y} \times x_i + y_i)}$,当然这个用点积的代数式可以直接得到,于是把每个向量看作一个一次函数,我们就可以自然地想到李超树来维护这个最值.
但是

  但是李超树很难支持撤销,我们考虑把撤销转化掉.一般这样的题目都有类似 “降维” 的思想,而这种操作一般通过数据结构实现.我们对操作的时间轴建一棵标记永久化的线段树,叶子结点存对应操作的信息,非叶子结点没有需要维护的信息,只需要记录懒标即可.而这个懒标在这个题里面就是一棵李超树,这个东西叫线段树分治.

  线段树分治又何尝不是一种树套树

  还剩下一个问题,李超树查询点值是个小数,怎么操作?我们把需要查询的点值离散化,并在离散化后的值域上建李超树即可,并把一次函数的 $f(x)$ 改为 $f(g(x))$,其中 $g(x)$ 是离散化后的编号到真实值域的映射.

  $O(n \log^2 n)$.

#include <iomanip>
#include <iostream>
#include <algorithm>
using std::cin;
using std::cout;
#define rep(i,a,b) for (int i(a); i<=(b); ++i)
#define drep(i,b,a) for (int i(b); i>=(a); --i)

int k[200100], b[200100];
int Id[200100], cnt;
double dat[200100], ver[200100]; // vertical coordination

double eval(int i, int x) {
    if (i == 0) return -1e18;
    return k[i] * dat[x] + b[i];
}

#define mid ((l+r)/2)
int tr[4000100], tid, lch[4000100], rch[4000100];
int upd(int u, int id, int l = 1, int r = cnt) {
    if (!u) return tr[u = ++tid] = id, u;
    if (eval(tr[u], mid) < eval(id, mid)) std::swap(tr[u], id);
    if (l < r && k[tr[u]] > k[id]) lch[u] = upd(lch[u], id, l, mid);
    else if (l < r) rch[u] = upd(rch[u], id, mid+1, r);
    return u;
}
double qry(int u, int x, int l = 1, int r = cnt) {
    if (!u) return -1e18;
    else if (l == r) return eval(tr[u], x);
    else if (mid >= x) return std::max(eval(tr[u], x), qry(lch[u], x, l, mid));
    else return std::max(eval(tr[u], x), qry(rch[u], x, mid+1, r));
}
#undef mid

int q, rt[800100], st[200100], T;
struct request {
    int op, x, y;
} req[200100];

#define mid ((l+r)/2)
void mark(int qL, int qR, int u = 1, int l = 1, int r = q) {
    if (l > qR || r < qL) return;
    else if (l >= qL && r <= qR) rt[u] = upd(rt[u], qL);
    else mark(qL, qR, u*2, l, mid), mark(qL, qR, u*2+1, mid+1, r);
}
double qry2(int id, int u = 1, int l = 1, int r = q) {
    if (l == r) return qry(rt[u], Id[id]);
    else if (mid >= id) return std::max(qry(rt[u], Id[id]), qry2(id, u*2, l, mid));
    else return std::max(qry(rt[u], Id[id]), qry2(id, u*2+1, mid+1, r));
}
#undef mid

signed main() {
    std::ios::sync_with_stdio(0);
    cin.tie(0);
    cout << std::fixed;
    cout << std::setprecision(0);
    cin >> q;
    rep(i,1,q) {
        cin >> req[i].op >> req[i].x;
        if (req[i].op == 1) {
            cin >> req[i].y;
            k[i] = req[i].x, b[i] = req[i].y;
        } else if (req[i].op == 3) {
            cin >> req[i].y;
            ver[i] = dat[++cnt] = 1.0*req[i].x/req[i].y;
        }
    }
    std::sort(dat+1, dat+cnt+1);
    cnt = std::unique(dat+1, dat+cnt+1)-dat-1;
    rep(i,1,q) {
        if (req[i].op == 1) {
            st[++T] = i;
        } else if (req[i].op == 2) {
            mark(st[req[i].x], i), st[req[i].x] = 0;
        } else {
            Id[i] = std::lower_bound(dat+1, dat+cnt+1, ver[i])-dat;
        }
    }
    rep(i,1,T)
        if (st[i]) mark(st[i], q);
    int now = 0;
    rep(i,1,q) {
        if (req[i].op == 1) now++;
        else if (req[i].op == 2) now--;
        else if (now == 0) cout << "0\n";
        else cout << req[i].y*qry2(i) << "\n";
    }
}

link.

首先有一个很厉害的转换,某序列若存在题目中定义的中位数,则该数一定是该序列的中位数.感觉这一步挺神仙,我没想出来。这个是原题的必要条件,要做成充要只需要用数据结构检验该数的出现次数是否超过序列的模的一半.

具体到维护,使用链表维护所有链的形态,使用动态开点权值线段树维护每条链上各个数的出现次数即可.

#include <list>
#include <cstdio>
#define rep(i,a,b) for (int i(a); i<=(b); ++i)
#define drep(i,b,a) for (int i(b); i>=(a); --i)

struct istream {
    istream& operator>>(int& n) {
        n = 0; char s = getchar(); while (s < '0' || s > '9') s = getchar();
        for (; s>='0'&&s<='9'; s=getchar()) n = n*10+s-48;
        return *this;
    }
} cin;

int n, q, m, rt[1000100], id[1000100];
std::list<int> ls[1000100];

long long sum[10500100];
int tid, lch[10500100], rch[10500100], now[1000100], rec[1000100];
#define mid ((l+r)>>1)
int upd(int u, int pos, int val, int l = 1, int r = n+q) {
    if (!u) u = ++tid;
    sum[u] += val;
    if (l < r && mid >= pos) lch[u] = upd(lch[u], pos, val, l, mid);
    else if (l < r) rch[u] = upd(rch[u], pos, val, mid+1, r);
    return u;
}
int mrg(int u, int v, int l = 1, int r = n+q) {
    if (!u || !v) return u+v;
    sum[u] += sum[v];
    lch[u] = mrg(lch[u], lch[v], l, mid);
    rch[u] = mrg(rch[u], rch[v], mid+1, r);
    return u;
}
long long qry(int u, int val, int l = 1, int r = n+q) {
    if (!u) return 0;
    else if (l == r) return sum[u];
    else if (mid >= val) return qry(lch[u], val, l, mid);
    else return qry(rch[u], val, mid+1, r);
}
int bisect(long long pos, int l = 1, int r = n+q) {
    if (l == r) return l;
    long long t = 0;
    rep(i,1,m) t += sum[lch[now[i]]];
    if (t >= pos) {
        rep(i,1,m) now[i] = lch[now[i]];
        return bisect(pos, l, mid);
    } else {
        rep(i,1,m) now[i] = rch[now[i]];
        return bisect(pos, mid+1, r);
    }
}
#undef mid

signed main() {
    cin >> n >> q;
    rep(i,1,n+q) id[i] = i;
    for (int i=1,l,x; i<=n; ++i) {
        cin >> l;
        rep(j,1,l) {
            cin >> x;
            rt[i] = upd(rt[i], x, 1);
            ls[id[i]].push_back(x);
        }
    }
    for (int op,x,y,z,Q=q; Q--;) {
        cin >> op >> x;
        if (op == 1) {
            cin >> y;
            ls[id[x]].push_back(y);
            rt[x] = upd(rt[x], y, 1);
        } else if (op == 2) {
            rt[x] = upd(rt[x], ls[id[x]].back(), -1);
            ls[id[x]].pop_back();
        } else if (op == 3) {
            long long U = 0, S = 0;
            m = x;
            rep(i,1,m) {
                cin >> y;
                now[i] = rec[i] = rt[y];
                U += ls[id[y]].size();
            }
            int M = bisect((U+1)/2);
            rep(i,1,m) S += qry(rec[i], M);
            if (S*2 > U) printf("%d\n", M);
            else puts("-1");
        } else { // append @x at the tail of @y
            cin >> y >> z;
            std::swap(x, y);
            rt[z] = mrg(rt[x], rt[y]);
            if (ls[id[x]].size() < ls[id[y]].size()) {
                while (!ls[id[x]].empty())
                    ls[id[y]].push_back(ls[id[x]].front()), ls[id[x]].pop_front();
                id[z] = id[y];
            } else {
                while (!ls[id[y]].empty())
                    ls[id[x]].push_front(ls[id[y]].back()), ls[id[y]].pop_back();
                id[z] = id[x];
            }
        }
    }
}

link.

不错的题.

改写条件.我们称一个区间 $[l, r)$ 是合法的当且仅当 $\forall i \in [l, r)$,有 $a_i \equiv k \pmod d$,$\not\exists j \in [l, r), j \neq i, s.t. a_j = a_i$,且 $\max_{[l, r)} - \min_{[l, r)} + l < r+k$.考虑扫描线枚举 $r$,现在我们就考虑维护 $f_i = \max_{[i, r)} - \min_{[i, r)} + i$.假设我们使用线段树来维护,那么在查询的时候使用线段树二分即可.现在考虑如何快速维护加入 $a_{i+1}$ 这个元素到线段树里面.

观察 $f_i$ 的结构,首先 $i$ 可以预处理进线段树,全程不需要动.以维护 max 为例,我们可以引入一个非严格递减的单调栈,单调栈中的每一个元素表示了原数组中一段的最大值.意即,这个单调栈中的元素充当了「哨兵」的角色,将原序列分成了若干的被统治区间.于是就可以简单维护了.

#include <cstdio>
#include <cassert>
#include <map>
#include <algorithm>
#include <utility>
using Pr = std::pair<int, int>;
#define rep(i,a,b) for (int i(a); i<=(b); ++i)
#define rep_(i,a,b) for (int i(a); i<(b); ++i)
#define drep(i,b,a) for (int i(b); i>=(a); --i)
inline int rint() {
    int n(0), f(1);
    char s(getchar());
    for (; s<'0'||s>'9'; s=getchar())
        if (s == '-') f = -1;
    for (; s>='0'&&s <= '9'; s=getchar()) n = n*10+s-48;
    return n * f;
}

const int kMaxN = 200100;
const int inf = std::numeric_limits<int>::max();
int n, K, d, a[kMaxN], stk1[kMaxN], stk2[kMaxN], T1, T2;

struct segment_tree {
#define mid ((l+r)/2)
    int lz[kMaxN*4], s[kMaxN*4];
    void build(int i, int l, int r) {
        if (l == r) s[i] = l;
        else build(i*2, l, mid), build(i*2+1, mid+1, r), s[i] = std::min(s[i*2], s[i*2+1]);
    }
    void pr(int i, int w) { lz[i] += w, s[i] += w; }
    void push(int i) {
        if (lz[i] != 0) pr(i*2, lz[i]), pr(i*2+1, lz[i]), lz[i] = 0;
    }
    void upd(int l, int r, int w) { upd(l, r, w, 1, 1, n); }
    void upd(int qL, int qR, int w, int i, int l, int r) {
        if (l >= qL && r <= qR) return pr(i, w);
        else {
            push(i);
            if (mid >= qL) upd(qL, qR, w, i*2, l, mid);
            if (mid < qR) upd(qL, qR, w, i*2+1, mid+1, r);
            s[i] = std::min(s[i*2], s[i*2+1]);
        }
    }
    int bisect(int l, int r, int up) { return bisect(l, r, up, 1, 1, n); }
    int bisect(int qL, int qR, int up, int i, int l, int r) {
        if (l == r) return l;
        push(i);
        if (l >= qL && r <= qR) {
            if (s[i*2] <= up) return bisect(qL, qR, up, i*2, l, mid);
            else if (s[i*2+1] <= up) return bisect(qL, qR, up, i*2+1, mid+1, r);
            return n+1;
        }
        int res = n+1;
        if (mid >= qL && s[i*2] <= up) res = bisect(qL, qR, up, i*2, l, mid);
        if (res <= n) return res;
        if (mid < qR && s[i*2+1] <= up) return bisect(qL, qR, up, i*2+1, mid+1, r);
        return n+1;
    }
#undef mid
} sgt;

Pr solve(int L, int R) {
    rep_(i,L,R) a[i] /= d;
    T1 = T2 = 0;
    int nl = L, nr = L, lb = L;
    std::map<int, int> tak;
    stk1[0] = stk2[0] = L-1;
    rep_(i,L,R) {
        while (T1 && a[stk1[T1]] < a[i]) // non-strictly decreasing stack
            sgt.upd(stk1[T1-1]+1, stk1[T1], a[i]-a[stk1[T1]]), T1--;
        stk1[++T1] = i;
        while (T2 && a[stk2[T2]] > a[i]) // non-strictly increasing stack
            sgt.upd(stk2[T2-1]+1, stk2[T2], a[stk2[T2]]-a[i]), T2--;
        stk2[++T2] = i;
        int j = sgt.bisect(lb = std::max(lb, tak[a[i]]+1), i, i+K);
        if (i-j+1 > nr-nl+1) nl = j, nr = i;
        tak[a[i]] = i;
    }
    return Pr(nl, nr+1);
}

signed main() {
#ifdef cirnovsky
    freopen("in.txt", "r", stdin);
#endif
    n = rint(), K = rint(), d = rint();
    rep(i,1,n) a[i] = rint()+1e9+1;
    rep(i,1,n) assert(a[i] >= 0);
    sgt.build(1, 1, n);
    a[n+1] = -1;
    if (d == 0) {
        int las = 1, wl = 1, wr = 2;
        rep(i,2,n+1)
            if (a[i] != a[i-1]) {
                if (i-las > wr-wl) wr = i, wl = las;
                las = i;
            }
        printf("%d %d\n", wl, wr-1);
    } else {
        int nl = 1, ansl = 1, ansr = 2;
        rep(nr,2,n+1) {
            if (nr == n+1 || a[nr]%d != a[nr-1]%d) {
                Pr ret = solve(nl, nr);
                nl = nr;
                if (ret.second-ret.first > ansr-ansl)
                    ansl = ret.first, ansr = ret.second;
            }
        }
        printf("%d %d\n", ansl, ansr-1);
    }
}
/* 
1. mod d 结果相同
2. 无重复
3. max[l, r] - min[l, r]
monotonic stacks
*/

link。

这篇题解比较随便。

基本是个一眼题,但是数组开小调了很久,主席树就只能造极限数据猜空间吗,,,,

反正,大于根号的就一个,跑个 hh 的项链,小于根号就那么几个,开个 ST 表维护极值就好。

#include <cassert>
#include <cstdio>
#include <algorithm>
#define rep(i,a,b) for (int i(a); i<=(b); ++i)
#define rep_(i,a,b) for (int i(a); i<(b); ++i)
#define drep(i,b,a) for (int i(b); i>=(a); --i)
inline int rint() {
    int n(0),f(1);char s=getchar();for(;s<'0'||s>'9';s=getchar())if(s=='-')f=-1;for(;s>='0'&&s<='9';s=getchar())n=n*10+s-48;return n*f;
}

const int kMaxN = 100100;
const int kMaxS = 6400100;
const int kMaxP = 87;
const int mod = 1e9+7;
const int pn[kMaxP] = {
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
    31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
    73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
    127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
    179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
    233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
    283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
    353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
    419, 421, 431, 433, 439, 443, 449
};
int n, a[kMaxN], lgs[kMaxN], pre[2*kMaxN], rt[kMaxN];

int pow(int n, int m) {
    int z(1);
    for (; m; m>>=1,n=1ll*n*n%mod)
        if (m&1) z = 1ll*z*n%mod;
    return z;
}
int inv(int n) {
    return pow(n, mod-2);
}

struct sparse_table {
    short f[20][kMaxN];
    void dp(int n) {
        rep(i,1,19)
            rep(j,1,n)
                f[i][j] = std::max(f[i-1][j], f[i-1][j+(1<<i-1)]);
    }
    int qry(int l, int r) {
        int k = lgs[r-l+1];
        return std::max(f[k][l], f[k][r-(1<<k)+1]);
    }
} tab[kMaxP];


struct segment_tree {
#define mid ((l+r)/2)
    int ls[kMaxS], rs[kMaxS], mul[kMaxS], tid;
    void build(int& p, int l, int r) {
        mul[p = ++tid] = 1;
        if (l != r) build(ls[p], l, mid), build(rs[p], mid+1, r);
    }
    void mdf(int pos, int val, int& p) { mdf(pos, val, p, 1, n); }
    void mdf(int pos, int val, int& p, int l, int r) {
        ls[++tid] = ls[p], rs[tid] = rs[p], mul[tid] = 1ll*mul[p]*val%mod;
        p = tid;
        if (l != r) {
            if (mid >= pos) mdf(pos, val, ls[p], l, mid);
            else mdf(pos, val, rs[p], mid+1, r);
        }
    }
    int qry(int l, int r, int p) { return qry(l, r, p, 1, n); }
    int qry(int qL, int qR, int p, int l, int r) {
        if (!p || l > qR || r < qL) return 1;
        else if (l >= qL && r <= qR) return mul[p];
        return 1ll*qry(qL, qR, ls[p], l, mid)*qry(qL, qR, rs[p], mid+1, r)%mod;
    }
#undef mid
} sgt;

void handle(int pos) {
    for (int i=0; i<kMaxP&&pn[i]<=a[pos]; ++i)
        for (; a[pos]%pn[i] == 0; a[pos]/=pn[i]) tab[i].f[0][pos]++;
}

signed main() {
#ifdef cirnovsky
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    rep_(i,2,kMaxN)
        lgs[i] = lgs[i/2]+1;
    n = rint();
    sgt.build(rt[0], 1, n);
    rep(i,1,n) a[i] = rint();
    rep(i,1,n) handle(i);
    rep_(i,0,kMaxP) tab[i].dp(n);
    rep(i,1,n) {
        rt[i] = rt[i-1];
        if (pre[a[i]]) sgt.mdf(pre[a[i]], inv(a[i]), rt[i]);
        sgt.mdf(i, a[i], rt[i]);
        pre[a[i]] = i;
    }
    int q = rint();
    for (int l,r,ans=0; q--;) {
        l = (rint()+ans)%n+1, r = (rint()+ans)%n+1;
        if (l > r) std::swap(l, r);
        ans = 1ll*sgt.qry(l, r, rt[r])*inv(sgt.qry(l, r, rt[l-1]))%mod;
        rep_(i,0,kMaxP)
            ans = 1ll*ans*pow(pn[i], tab[i].qry(l, r))%mod;
        printf("%d\n", ans);
    }
}

link。

文章抄袭于 曾经的 cq 之光

先咕了,睡觉比较重要。

#include <iostream>
#include <numeric>
#define rep(i,l,r) for (int i=l;i<=(r);++i)
#define drep(i,r,l) for (int i=(r);i>=(l);--i)
inline int read() {
    int n = 0, f = 1;
    char s;
    for (; s<'0'||s>'9'; s=getchar())
        if (s == '-') f = -1;
    for (; s>='0'&&s<='9'; s=getchar())
        n = n*10+s-48;
    return n*f;
}

const int kMaxN = 1<<18;
int n, m, a[kMaxN], f[kMaxN];

namespace ufs {
    int fa[kMaxN];
    void init(int n) {
        std::iota(fa+1, fa+n+1, 1);
    }
    inline int find(int u) {
        while (u != fa[u])
            u = fa[u] = fa[fa[u]];
        return u;
    }
    bool merge(int u, int v) {
        u = find(u), v = find(v);
        if (u != v) {
            fa[u] = v;
            return 1;
        }
        return 0;
    }
}

signed main() {
    n = read(), m = read();
    ufs::init((1<<m)-1);
    long long ans;
    rep(i,1,n) {
        a[i] = read();
        if (!f[a[i]]) f[a[i]] = a[i];
        else ans += a[i];
    }
    drep(i,(1<<m)-1,0) {
        for (int j=0; j<m&&(!f[i]); ++j)
            f[i] = f[i|(1<<j)];
        rep(j,0,m-1)
            if (f[i] && !(i>>j&1) && f[i|(1<<j)] &&
                ufs::merge(f[i], f[i|(1<<j)]))
                ans += i;
    }
    printf("%lld\n", ans);
}