Solutions - 集训第二轮杂题选讲

D - Qoj9986 Shiori

一个比较好的势能分析题。

对于 op = 1 我们直接赋值。对于 op = 2 我们暴力枚举每个值,如果不存在该值那么这个东西就是 \(\text{mex}\),加上去;否则,将所有该值的极大同色节点弄出来加上 \(\text{mex}\)。对于 op = 3,解法显然。

我们考虑只有 op = 2 的情况,发现只有 \(a_i \le n\) 的节点有可能被更新。又发现每次有效更新中 \(a_i \gets a_i+k, k > a_i\),即 \(a_i\) 至少是之前的两倍,也就是说一个节点最多只能被有效更新 \(\log n\) 次。又发现 op = 1 最多会带来 \(q \log n\) 个新的极大同色节点,线段树操作为 \(\log\),那么复杂度为 \(\mathrm O(n \log n + q \log^2 n)\)

#include <bits/stdc++.h>
#define llong long long
#define N 500005
using namespace std;

#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
    x = 0; int w = 1;
    char ch = gc();
    while(ch < '0' || ch > '9'){
        if(ch == '-') w = -w;
        ch = gc();
    }
    while(ch >= '0' && ch <= '9')
        x = (x<<3)+(x<<1)+(ch^48), ch = gc();
    x *= w;
}
template<typename T, typename... Args>
inline void read(T& x, Args& ...y){
    return read(x), read(y...);
}

constexpr llong inf = 1e9+7;

int n, q;
llong a[N];

struct Tag{
    llong t1, t2;
    Tag(){t1 = -1, t2 = 0;}
    Tag(llong tg1, llong tg2){t1 = tg1, t2 = tg2;}
    Tag& operator+=(Tag o){
        if(o.t1 != -1){
            t1 = o.t1, t2 = 0;
        }
        if(o.t2) t2 += o.t2;
        return *this;
    }
    Tag& operator+=(int o){
        t2 += o;
        return *this;
    }
    Tag& operator=(int o){
        t1 = o, t2 = 0;
        return *this;
    }
};
Tag tag[N<<2];
struct Node{
    int l, r;
    llong minn, maxn, sum;
    Node operator+(Node o){
        return {l, o.r, min(minn, o.minn), max(maxn, o.maxn), sum+o.sum};
    }
    Node& operator+=(Tag o){
        if(o.t1 != -1){
            minn = maxn = o.t1;
            sum = o.t1*(r-l+1);
        }
        if(o.t2){
            minn += o.t2, maxn += o.t2;
            sum += o.t2*(r-l+1);
        }
        return *this;
    }
    Node& operator+=(int o){
        minn += o, maxn += o;
        sum += 1ll*o*(r-l+1);
        return *this;
    }
    Node& operator=(int o){
        minn = maxn = o;
        sum = 1ll*o*(r-l+1);
        return *this;
    }
};
Node val[N<<2];
vector<pair<int, int>> his;

#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define mid ((l+r)>>1)

inline void build(int x = 1, int l = 1, int r = n){
    if(l == r) return val[x] = {l, l, a[l], a[l], a[l]}, void();
    build(ls(x), l, mid), build(rs(x), mid+1, r);
    val[x] = val[ls(x)]+val[rs(x)];
    return;
}
inline void pushdown(int x){
    val[ls(x)] += tag[x], tag[ls(x)] += tag[x];
    val[rs(x)] += tag[x], tag[rs(x)] += tag[x];
    tag[x] = Tag();
}
inline void assign(int L, int R, int k, int x = 1){
    int l = val[x].l, r = val[x].r;
    if(L <= l && R >= r) return val[x] = k, tag[x] = k, void();
    pushdown(x);
    if(L <= mid) assign(L, R, k, ls(x));
    if(R >  mid) assign(L, R, k, rs(x));
    val[x] = val[ls(x)]+val[rs(x)];
    return;
}
inline void add(int L, int R, int k, int x = 1){
    int l = val[x].l, r = val[x].r;
    if(L <= l && R >= r) return val[x] += k, tag[x] += k, void();
    pushdown(x);
    if(L <= mid) add(L, R, k, ls(x));
    if(R >  mid) add(L, R, k, rs(x));
    val[x] = val[ls(x)]+val[rs(x)];
    return;
}
inline void makemex(int L, int R, int k, int x = 1){
    if(val[x].minn > k) return;
    int l = val[x].l, r = val[x].r;
    if(L <= l && R >= r && val[x].maxn == k){
        his.emplace_back(l, r);
        val[x] += inf, tag[x] += inf;
        return;
    }
    pushdown(x);
    if(L <= mid) makemex(L, R, k, ls(x));
    if(R >  mid) makemex(L, R, k, rs(x));
    val[x] = val[ls(x)]+val[rs(x)];
    return;
}
inline llong getmin(int L, int R, int x = 1){
    int l = val[x].l, r = val[x].r;
    if(L <= l && R >= r) return val[x].minn;
    pushdown(x);
    if(R <= mid) return getmin(L, R, ls(x));
    if(L >  mid) return getmin(L, R, rs(x));
    return min(getmin(L, R, ls(x)), getmin(L, R, rs(x)));
}
inline llong getsum(int L, int R, int x = 1){
    int l = val[x].l, r = val[x].r;
    if(L <= l && R >= r) return val[x].sum;
    pushdown(x);
    if(R <= mid) return getsum(L, R, ls(x));
    if(L >  mid) return getsum(L, R, rs(x));
    return getsum(L, R, ls(x))+getsum(L, R, rs(x));
}

int main(){
    read(n, q);
    for(int i = 1; i <= n; ++i) read(a[i]);
    build();
    while(q--){
        int op, l, r, k;
        read(op, l, r);
        if(op == 1){
            read(k);
            assign(l, r, k);
        }
        if(op == 2){
            int mex = 0;
            while(getmin(l, r) == mex){
                makemex(l, r, mex);
                ++mex;
            }
            for(auto now : his){
                int ll = now.first, rr = now.second;
                add(ll, rr, -inf);
            }
            add(l, r, mex);
            his.clear();
        }
        if(op == 3)
            printf("%lld\n", getsum(l, r));
    }
    return 0;
}

E - P4770 [NOI2018] 你的名字

我觉得我没有完全理解这个题,云里雾里就过了。

我们先考虑 \(l = 1, r = n\) 的情况。我们对 S 和 T 建出 SAM,考虑维护 T 的每个状态对应的节点在 S 上匹配的最长长度 \(l_i\),那么对于这个节点就有 \(len_i - \max\{l_i, len_{fa_i}\}\) 个串没有贡献。我们维护一个在 S 上的指针 \(x\) 和 T 上的指针 \(y\) 和一个当前匹配长度 \(cnt\),对于 T 上的一个字符 \(c\),将 \(y\) 跳一下,如果 \(x\)\(c\) 的转移就 \(cur \gets cur+1, l_y \gets \max\{l_y, cur \}\),如果 \(x\) 没有 \(c\) 的转移就一直跳 fail 直到有转移并且 \(cur \gets len_{fa_u}\),然后 \(cur \gets cur+1, l_y \gets \max\{l_y, cur \}\)。最后 dfs 一下 \(y\) 的 parent 树,将 \(l_y\) 取到所有儿子的最大值,与 \(len_y\)\(\min\) 即可。

然后考虑 \(l, r\) 任意的情况。我们不太能对任一区间建出 SAM,使用回滚莫队也是不现实的因为 SAM 不支持双端加字符。于是我们考虑对整个 S 建出 SAM 进行匹配。考虑使用线段树合并求 \(\text{endpos}\)\(y\) 还是在 T 上正常跳,然后对于 \(x\) 试图得到合法且最长的 \(cur\) 进行转移即可。

复杂度 \(\mathrm O(n \log n)\)

#include <bits/stdc++.h>
#define llong long long
#define N 1000006
using namespace std;

#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
    x = 0;
    char ch = gc();
    while(ch < '0' || ch > '9') ch = gc();
    while(ch >= '0' && ch <= '9')
        x = (x<<3)+(x<<1)+(ch^48), ch = gc();
}
inline void read(char* x){
    *++x = gc();
    while(*x == ' ' || *x == '\r' || *x == '\n') *x = gc();
    while(*x != ' ' && *x != '\r' && *x != '\n') *++x = gc();
    *x = '\0';
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
    return read(x), read(y...);
}

int n, q;
char a[N], b[N];

struct SAM{
    int l;
    int fa[N<<1], len[N<<1], nxt[N<<1][32], tsiz;
    int tag[N<<1];

    SAM(){
        tsiz = 1;
        return;
    }
    inline void clear(){
        for(int i = 1; i <= tsiz; ++i) fa[i] = len[i] = 0;
        for(int i = 1; i <= tsiz; ++i)
            for(int j = 1; j <= 26; ++j) nxt[i][j] = 0;
        tsiz = 1;
        return;
    }
    inline int insert(int c, int lst){
        int x = lst, cur = ++tsiz;
        len[cur] = len[lst]+1;
        while(x && !nxt[x][c])
            nxt[x][c] = cur, x = fa[x];
        if(!x){
            fa[cur] = 1;
            return cur;
        }
        int y = nxt[x][c];
        if(len[y] == len[x]+1){
            fa[cur] = y;
        }
        else{
            int z = ++tsiz;
            len[z] = len[x]+1, fa[z] = fa[y];
            for(int i = 1; i <= 26; ++i)
                nxt[z][i] = nxt[y][i];
            while(x && nxt[x][c] == y)
                nxt[x][c] = z, x = fa[x];
            fa[y] = fa[cur] = z;
        }
        return cur;
    }
    inline void insert(char* a, int n){
        l = n;
        int x = 1;
        for(int i = 1; i <= l; ++i){
            x = insert(a[i]^96, x);
            tag[x] = i;
        }
        return;
    }
    
};
SAM sam1, sam2;

int val[N<<6], ls[N<<6], rs[N<<6], ssiz, root[N];
vector<int> G[N<<1];

#define mid ((l+r)>>1)
inline int cpynode(int x){
    int y = ++ssiz;
    val[y] = val[x], ls[y] = ls[x], rs[y] = rs[x];
    return y;
}
inline void modify(int pos, int &x, int l = 1, int r = sam1.l){
    x = cpynode(x);
    if(l == r) return val[x] = l, void();
    if(pos <= mid) modify(pos, ls[x], l, mid  );
    else           modify(pos, rs[x], mid+1, r);
    val[x] = max(val[ls[x]], val[rs[x]]);
    return;
}
inline int merge(int x, int y){
    if(!x || !y) return x|y;
    int z = cpynode(x);
    ls[z] = merge(ls[x], ls[y]);
    rs[z] = merge(rs[x], rs[y]);
    val[z] = max(val[ls[z]], val[rs[z]]);
    return z;
}
inline int query(int L, int R, int x, int l = 1, int r = sam1.l){
    if(!x) return 0;
    if(L <= l && R >= r) return val[x];
    if(R <= mid) return query(L, R, ls[x], l, mid  );
    if(L >  mid) return query(L, R, rs[x], mid+1, r);
    return max(query(L, R, ls[x], l, mid), query(L, R, rs[x], mid+1, r));
}
#undef mid

inline void mergeup(int u){
    for(int v : G[u])
        mergeup(v), root[u] = merge(root[u], root[v]);
    return;
}
inline void prework(){
    for(int i = 2; i <= sam1.tsiz; ++i)
        G[sam1.fa[i]].push_back(i);
    for(int i = 1; i <= sam1.tsiz; ++i)
        if(sam1.tag[i]) modify(sam1.tag[i], root[i]);
    mergeup(1);
    return;
}

int tmp[N<<1];
vector<int> G2[N<<1];
inline void dfs(int u){
    for(int v : G2[u])
        dfs(v), tmp[u] = max(tmp[u], tmp[v]);
    tmp[u] = min(tmp[u], sam2.len[u]);
    return;
}

int main(){
    read(a), n = strlen(a+1);
    sam1.insert(a, n), prework();
    read(q);
    while(q--){
        int l, r, len;
        read(b, l, r);
        len = strlen(b+1);
        sam2.clear(), sam2.insert(b, len);
        for(int i = 1; i <= sam2.tsiz; ++i) 
            tmp[i] = 0, G2[i].clear();
        int x = 1, y = 1, cnt = 0;
        for(int i = 1; i <= len; ++i){
            while(y && !sam2.nxt[y][b[i]^96]) y = sam2.fa[y];
            if(y) y = sam2.nxt[y][b[i]^96];
            else y = 1;
            int nxtx = sam1.nxt[x][b[i]^96];
            if(query(1, r, root[nxtx]) >= l+cnt){
                ++cnt;
                x = nxtx;
                tmp[y] = max(tmp[y], cnt);
                continue;
            }
            while(x && (!nxtx || query(1, r, root[nxtx]) < l+sam1.len[sam1.fa[x]]))
                x = sam1.fa[x], nxtx = sam1.nxt[x][b[i]^96];
            if(!x){
                x = 1;
                cnt = 0;
            }
            else{
                cnt = min(sam1.len[x]+1, query(1, r, root[nxtx])-l+1);
                x = nxtx;
                tmp[y] = max(tmp[y], cnt);
            }
        }
        for(int i = 1; i <= sam2.tsiz; ++i)
            G2[sam2.fa[i]].push_back(i);
        dfs(1);
        llong res = 0;
        for(int i = 1; i <= sam2.tsiz; ++i)
            res += sam2.len[i]-max(tmp[i], sam2.len[sam2.fa[i]]);
        printf("%lld\n", res);
    }
    return 0;
}

F - P3674 小清新人渣的本愿

这题是因为太水了被开除 Ynoi 籍了吗。

考虑对于全局怎么做。做法是显然的,对于 op = 1,2 我们建出 \(a_i\)\(-a_i+\Delta\) 的 bitset 然后询问的时候搞一下即可。对于 op = 3 我们直接枚举约数即可。

考虑区间查询,由于这是 lxl 的题,我们直接使用莫队。

复杂度 \(\mathrm(q \sqrt n + \frac{qn}{\omega})\)

#include <bits/stdc++.h>
#define llong long long
#define N 100005
using namespace std;

#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
    x = 0; int w = 1;
    char ch = gc();
    while(ch < '0' || ch > '9'){
        if(ch == '-') w = -w;
        ch = gc();
    }
    while(ch >= '0' && ch <= '9')
        x = (x<<3)+(x<<1)+(ch^48), ch = gc();
    x *= w;
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
    return read(x), read(y...);
}

constexpr int B = 316, M = 1e5;

int n, q;
int a[N];
struct Query{
    int op;
    int l, r, x;
    int bl, id;
    bool operator<(const Query& o)const{
        return bl<o.bl || (bl==o.bl && ((~bl&1)^(r<o.r)));
    }
};
Query qry[N];
bool ans[N];

bitset<N> b1, b2;
int cnt[N];

#define add(x) (cnt[x]++ == 0 ? b1[x] = b2[M-x] = 1 : 0)
#define del(x) (--cnt[x] == 0 ? b1[x] = b2[M-x] = 0 : 1)

int main(){
    read(n, q);
    for(int i = 1; i <= n; ++i) read(a[i]);
    for(int i = 1; i <= q; ++i){
        read(qry[i].op, qry[i].l, qry[i].r, qry[i].x);
        qry[i].bl = qry[i].l/B, qry[i].id = i;
    }
    sort(qry+1, qry+q+1);
    int lp = 1, rp = 0;
    for(int i = 1; i <= q; ++i){
        int l = qry[i].l, r = qry[i].r;
        while(lp > l) --lp, add(a[lp]);
        while(rp < r) ++rp, add(a[rp]);
        while(lp < l) del(a[lp]), ++lp;
        while(rp > r) del(a[rp]), --rp;
        int k = qry[i].x, id = qry[i].id;
        if(qry[i].op == 1) ans[id] = (b1&(b1>>k)).any();
        if(qry[i].op == 2) ans[id] = (b1&(b2>>(M-k))).any();
        if(qry[i].op == 3){
            int sq = ceil(sqrt(k));
            for(int j = 1; j <= sq; ++j){
                if(k % j || !cnt[j] || !cnt[k/j]) continue;
                ans[id] = true;
                break;
            }
        }
    }
    for(int i = 1; i <= q; ++i)
        puts(ans[i] ? "hana" : "bi");
    return 0;
}

G - Qoj7523 Partially Free Meal

决策单调性题,成功记忆恢复。

我们考虑对于单个 \(k\) 的解法。我们先按 \(b\) 排序,然后枚举每个 \(b\),我们找出所有 \(b\)\(b_i\) 小的前 \(k\)\(a_i\) 之和即可。

考虑每个 \(k\)。设 \(s(i, k)\) 为所有 \(b\)\(b_i\) 小的前 \(k\)\(a_i\) 之和,发现使用主席树可以 \(\mathrm O(n \log n) - \mathrm O(\log n)\) 求出 \(s\)。又发现对于一个位置 \(i\)\([1, i+1]\) 的第 \(k\) 大数不大于 \([1, i]\) 的第 \(k\) 大数,即 \(s(i, k)-s(i, k-1) \ge s(i+1, k)-s(i+1, k-1)\),即 \(s(i, k-1)+s(i+1, k) \le s(i, k)+s(i+1, k-1)\),即 \(s\) 满足四边形不等式。设 \(c(i, k) = \sum_{i=1}^k a_i + b_i\),发现 \(c(i, k-1)+c(i+1, k) = s(i, k-1)+s(i+1, k)+b_i+b_{i+1} \le s(i, k)+s(i+1, k-1)+b_i+b_{i+1} = c(i, k)+c(i+1, k-1)\),即 \(c\) 满足四边形不等式,于是 \(\mathrm{opt}\ c\) 单调。

于是整体二分即可。\(\mathrm O(n \log^2 n)\)

#include <bits/stdc++.h>
#define llong long long
#define N 200005
using namespace std;

#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
    x = 0; int w = 1;
    char ch = gc();
    while(ch < '0' || ch > '9'){
        if(ch == '-') w = -w;
        ch = gc();
    }
    while(ch >= '0' && ch <= '9')
        x = (x<<3)+(x<<1)+(ch^48), ch = gc();
    x *= w;
}
template<typename T, typename ...Args>
inline void read(T& x, Args& ...y){
    return read(x), read(y...);
}

int n;
struct Item{
    llong a, b;
};
Item a[N];
llong tmp[N], cnt;
llong ans[N];

struct Node{
    llong val, siz;
    inline Node operator+(Node o){
        return {val+o.val, siz+o.siz};
    }
    inline Node& operator+=(int o){
        val += tmp[o], ++siz;
        return *this;
    }
};
Node T[N<<5];
int ls[N<<5], rs[N<<5], root[N], tsiz;

#define mid ((l+r)>>1)
inline int cpynode(int x){
    int y = ++tsiz;
    T[y] = T[x], ls[y] = ls[x], rs[y] = rs[x];
    return y;
}
inline void modify(int pos, int& x, int l = 1, int r = cnt){
    x = cpynode(x);
    if(l == r) return T[x] += pos, void();
    if(pos <= mid) modify(pos, ls[x], l, mid  );
    else           modify(pos, rs[x], mid+1, r);
    T[x] = T[ls[x]]+T[rs[x]];
    return;
}
inline llong query(int k, int x, int l = 1, int r = cnt){
    if(l == r) return k*tmp[l];
    if(T[ls[x]].siz >= k) return query(k, ls[x], l, mid);
    else return T[ls[x]].val+query(k-T[ls[x]].siz, rs[x], mid+1, r);
}
#undef mid

inline void solve(int L, int R, int l, int r){
    if(L > R) return;
    int mid = (L+R)>>1, k = -1;
    ans[mid] = (llong)1e18+3;
    // if(mid % 1000 == 0) cerr << mid << endl;
    for(int i = max(l, mid); i <= r; ++i){
        llong res = query(mid, root[i])+a[i].b;
        if(res < ans[mid]) ans[mid] = res, k = i;
    }
    assert(k != -1);
    solve(L, mid-1, l, k);
    solve(mid+1, R, k, r);
    return;
}

int main(){
    read(n);
    for(int i = 1; i <= n; ++i) read(a[i].a, a[i].b);
    sort(a+1, a+n+1, [&](Item o1, Item o2){return o1.b<o2.b;});
    for(int i = 1; i <= n; ++i) tmp[++cnt] = a[i].a;
    sort(tmp+1, tmp+cnt+1), cnt = unique(tmp+1, tmp+cnt+1)-tmp-1;
    for(int i = 1; i <= n; ++i)
        a[i].a = lower_bound(tmp+1, tmp+cnt+1, a[i].a)-tmp;
    for(int i = 1; i <= n; ++i){
        root[i] = root[i-1];
        modify(a[i].a, root[i]);
    }
    solve(1, n, 1, n);
    for(int i = 1; i <= n; ++i)
        printf("%lld\n", ans[i]);
    return 0;
}

K - P10144 [WC2024] 水镜

很好的题目,很有意境的标题。做法三千,只取一种写。

由于题目中只出现了 \(2L\),我们以 \(L\) 指代 \(2L\)

我们维护对于两相邻点,合理的 \(L\) 的区间,然后使用 SegT 做这个东西即可。好像没了。

\(\mathrm O(n \log n)\)

#include <bits/stdc++.h>
#define llong long long
#define N 500005
using namespace std;

#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
    x = 0; int w = 1;
    char ch = gc();
    while(ch < '0' || ch > '9'){
        if(ch == '-') w = -w;
        ch = gc();
    }
    while(ch >= '0' && ch <= '9')
        x = (x<<3)+(x<<1)+(ch^48), ch = gc();
    x *= w;
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
    return read(x), read(y...);
}

constexpr llong inf = (llong)1e18+3;

int n;
llong a[N];
llong ans;

struct Node{
    int l, r;
    llong L[2][2], R[2][2];
    Node(){
        L[0][0] = L[1][1] = R[0][1] = R[1][0] = -inf;
        R[0][0] = R[1][1] = L[0][1] = L[1][0] =  inf;
    }
    Node(int pos){
        l = r = pos;
        L[0][0] = L[1][1] = R[0][1] = R[1][0] = -inf;
        R[0][0] = R[1][1] = L[0][1] = L[1][0] =  inf;
    }
    Node operator+(Node o){
        Node res;
        // 0 0
        res.l = l, res.r = o.r;
        res.L[0][0] = res.L[0][1] = res.L[1][0] = res.L[1][1] =  inf;
        res.R[0][0] = res.R[0][1] = res.R[1][0] = res.R[1][1] = -inf;
        if(a[r] < a[o.l]){
            res.L[0][0] = min(res.L[0][0], max(L[0][0], o.L[0][0]));
            res.L[0][1] = min(res.L[0][1], max(L[0][0], o.L[0][1]));
            res.L[1][0] = min(res.L[1][0], max(L[1][0], o.L[0][0]));
            res.L[1][1] = min(res.L[1][1], max(L[1][0], o.L[0][1]));
            res.R[0][0] = max(res.R[0][0], min(R[0][0], o.R[0][0]));
            res.R[0][1] = max(res.R[0][1], min(R[0][0], o.R[0][1]));
            res.R[1][0] = max(res.R[1][0], min(R[1][0], o.R[0][0]));
            res.R[1][1] = max(res.R[1][1], min(R[1][0], o.R[0][1]));
        }
        // 0 1
        if(a[r] > a[o.l]){
            res.L[0][0] = min(res.L[0][0], max(L[0][1], o.L[1][0]));
            res.L[0][1] = min(res.L[0][1], max(L[0][1], o.L[1][1]));
            res.L[1][0] = min(res.L[1][0], max(L[1][1], o.L[1][0]));
            res.L[1][1] = min(res.L[1][1], max(L[1][1], o.L[1][1]));
            res.R[0][0] = max(res.R[0][0], min(R[0][1], o.R[1][0]));
            res.R[0][1] = max(res.R[0][1], min(R[0][1], o.R[1][1]));
            res.R[1][0] = max(res.R[1][0], min(R[1][1], o.R[1][0]));
            res.R[1][1] = max(res.R[1][1], min(R[1][1], o.R[1][1]));
        }
        // 0 1
        res.L[0][0] = min(res.L[0][0], max({L[0][0], o.L[1][0], a[r]+a[o.l]}));
        res.L[0][1] = min(res.L[0][1], max({L[0][0], o.L[1][1], a[r]+a[o.l]}));
        res.L[1][0] = min(res.L[1][0], max({L[1][0], o.L[1][0], a[r]+a[o.l]}));
        res.L[1][1] = min(res.L[1][1], max({L[1][0], o.L[1][1], a[r]+a[o.l]}));
        res.R[0][0] = max(res.R[0][0], min(R[0][0], o.R[1][0]));
        res.R[0][1] = max(res.R[0][1], min(R[0][0], o.R[1][1]));
        res.R[1][0] = max(res.R[1][0], min(R[1][0], o.R[1][0]));
        res.R[1][1] = max(res.R[1][1], min(R[1][0], o.R[1][1]));
        // 1 0
        res.L[0][0] = min(res.L[0][0], max(L[0][1], o.L[0][0]));
        res.L[0][1] = min(res.L[0][1], max(L[0][1], o.L[0][1]));
        res.L[1][0] = min(res.L[1][0], max(L[1][1], o.L[0][0]));
        res.L[1][1] = min(res.L[1][1], max(L[1][1], o.L[0][1]));
        res.R[0][0] = max(res.R[0][0], min({R[0][1], o.R[0][0], a[r]+a[o.l]}));
        res.R[0][1] = max(res.R[0][1], min({R[0][1], o.R[0][1], a[r]+a[o.l]}));
        res.R[1][0] = max(res.R[1][0], min({R[1][1], o.R[0][0], a[r]+a[o.l]}));
        res.R[1][1] = max(res.R[1][1], min({R[1][1], o.R[0][1], a[r]+a[o.l]}));
        return res;
    }
    llong getL(){
        return min({L[0][0], L[0][1], L[1][0], L[1][1]});
    }
    llong getR(){
        return max({R[0][0], R[0][1], R[1][0], R[1][1]});
    }
};
Node val[N<<2];

#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define mid ((l+r)>>1)

inline void build(int x = 1, int l = 1, int r = n){
    if(l == r) return val[x] = Node(l), void();
    build(ls(x), l, mid), build(rs(x), mid+1, r);
    val[x] = val[ls(x)]+val[rs(x)];
    return;
}
inline Node query(int L, int R, int x = 1, int l = 1, int r = n){
    if(L <= l && R >= r) return val[x];
    if(R <= mid) return query(L, R, ls(x), l, mid  );
    if(L >  mid) return query(L, R, rs(x), mid+1, r);
    return query(L, R, ls(x), l, mid)+query(L, R, rs(x), mid+1, r);
}

int main(){
    read(n);
    for(int i = 1; i <= n; ++i) read(a[i]);
    build();
    for(int i = 1, j = 1; i <= n; ++i){
        while(j < n){
            Node res = query(i, j+1);
            if(res.getL() >= res.getR()) break;
            ++j;
        }
        ans += j-i;
    }
    printf("%lld", ans);
    return 0;
}

L - Qoj10288 Now or Never

题目名称暗示做法这一块。

考虑一个贪心做法。从前往后枚举位。如果我们能把当前位及以后的位都消成 \(0\) 就消,否则如果能将当前位变成 \(1\) 就变。

用线性基维护,\(\mathrm O(\frac{nm^2 + qm^2}{\omega})\)

#include <bits/stdc++.h>
#define llong long long
#define N 2005
using namespace std;

#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
    x = 0; int w = 1;
    char ch = gc();
    while(ch < '0' || ch > '9') ch = gc();
    while(ch >= '0' && ch <= '9')
        x = (x<<3)+(x<<1)+(ch^48), ch = gc();
    x *= w;
}
inline void read(char* x){
    *x = gc();
    while(*x == ' ' || *x == '\r' || *x == '\n') *x = gc();
    while(*x != ' ' && *x != '\r' && *x != '\n') *++x = gc();
    *x = '\0';
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
    return read(x), read(y...);
}

int n, m, q;
bitset<N> b[N], sum[N], msk[N];
int vis[N];
bitset<N> tmp;

char s[N];

int main(){
    read(n, m, q);
    for(int i = 1; i <= n; ++i){
        read(s), tmp = 0;
        for(int j = 0; j < m; ++j)
            if(s[j] == '1') tmp[j] = 1;
        for(int j = 0; j < m && tmp.any(); ++j){
            if(tmp[j]){
                if(vis[j]) tmp ^= b[j];
                else{
                    b[j] = tmp;
                    vis[j] = true;
                    break;
                }
            }
        }
    }
    bitset<N> one = 1;
    for(int i = m-1; ~i; --i)
        msk[i] = msk[i+1]|(one<<i);
    for(int i = m-1; ~i; --i){
        if(!vis[i]) continue;
        for(int j = i-1; ~j; --j)
            if(vis[j] && b[j][i]) b[j] ^= b[i];
    }
    while(q--){
        read(s), tmp = 0;
        for(int j = 0; j < m; ++j)
            if(s[j] == '1') tmp[j] = 1;
        for(int j = m-1; ~j; --j){
            sum[j] = sum[j+1];
            if(vis[j] && tmp[j]) sum[j] ^= b[j];
        }
        for(int j = 0; j < m; ++j){
            if((tmp&msk[j]) == sum[j]){
                tmp ^= sum[j];
                break;
            }
            else if(!tmp[j] && vis[j]) tmp ^= b[j];
        }
        for(int j = 0; j < m; ++j)
            putchar_unlocked(tmp[j]^48);
        putchar_unlocked('\n');
    }
    return 0;
}

Fun Fact:Hootime 寻找此题 AC 代码时发现找不到这道题的代码,当时 Hootime 以为自己罹患了妄想症。然后 Hootime 发现自己存在了 Luogu 题号的对应文件里。

M - Qoj5414 Stop, Yesterday Please No More

往日不再重现是吧。

这题是时,不想讲了。

#include <bits/stdc++.h>
#define llong long long
#define N 1003
using namespace std;

#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
    x = 0; int w = 1;
    char ch = gc();
    while(ch < '0' || ch > '9'){
        if(ch == '-') w = -w;
        ch = gc();
    }
    while(ch >= '0' && ch <= '9')
        x = (x<<3)+(x<<1)+(ch^48), ch = gc();
    x *= w;
}
inline void read(char* x){
    *++x = gc();
    while(*x == ' ' || *x == '\r' || *x == '\n') *x = gc();
    while(*x != ' ' && *x != '\r' && *x != '\n') *++x = gc();
    *x = '\0';
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
    return read(x), read(y...);
}

int n, m, k, l;
int minx, miny, maxx, maxy;
char a[N*N];
int mem1[N<<1][N<<1], mem2[N<<1][N<<1], mem3[N<<1][N<<1], mem4[N<<1][N<<1];

#define cnt(x,y)  mem1[(x)+N][(y)+N]
#define pre1(x,y) mem2[(x)+N][(y)+N]
#define pre2(x,y) mem3[(x)+N][(y)+N]
#define res(x,y)  mem4[(x)+N][(y)+N]

int _main(){
    read(n, m, k);
    read(a);
    l = strlen(a+1);
    int x = 0, y = 0;
    minx = miny = maxx = maxy = 0;
    cnt(0, 0) = 1;
    for(int i = 1; i <= l; ++i){
        if(a[i] == 'U') ++x;
        if(a[i] == 'D') --x;
        if(a[i] == 'L') ++y;
        if(a[i] == 'R') --y;
        minx = min(minx, x), maxx = max(maxx, x);
        miny = min(miny, y), maxy = max(maxy, y);
        if(x >= -n && x <= n && y >= -m && y <= m) cnt(x, y) |= 1;
    }
    for(int i = -n; i <= n; ++i){
        for(int j = -m; j <= m; ++j){
            pre1(i, j) = pre1(i, j-1)+cnt(i, j);
            pre2(i, j) = pre2(i-1, j)+cnt(i, j);
        }
    }
    int lx = 1+maxx, rx = n+minx;
    int ly = 1+maxy, ry = m+miny;
    int n2 = rx-lx, m2 = ry-ly;
    int res = 0;
    if(lx>rx || ly>ry){
        printf("%d\n", n*m*(k==0));
        goto clear;
    }
    res(0, 0) = 0;
    for(int i = 0; i <= n2; ++i)
        res(0, 0) += pre1(i, m2)-pre1(i, -1);
    for(int i = 1; i <= n-lx; ++i)
        res(i, 0) = res(i-1, 0)-(pre1(n2-i+1,m2)-pre1(n2-i+1,-1))+(pre1(-i,m2)-pre1(-i,-1));
    for(int i = -1; i >= -(lx-1); --i)
        res(i, 0) = res(i+1, 0)+(pre1(n2-i,m2)-pre1(n2-i,-1))-(pre1(-i-1,m2)-pre1(-i-1,-1));
    for(int i = 1-lx; i <= n-lx; ++i){
        for(int j = 1; j <= m-ly; ++j)
            res(i, j) = res(i,j-1)-(pre2(n2-i,m2-j+1)-pre2(-i-1,m2-j+1))+(pre2(n2-i,-j)-pre2(-i-1,-j));
        for(int j = -1; j >= -(ly-1); --j)
            res(i, j) = res(i,j+1)+(pre2(n2-i,m2-j)-pre2(-i-1,m2-j))-(pre2(n2-i,-j-1)-pre2(-i-1,-j-1));
    }
    for(int i = 1-lx; i <= n-lx; ++i)
        for(int j = 1-ly; j <= m-ly; ++j)
            res += ((n2+1)*(m2+1)-res(i,j) == k);
    printf("%d\n", res);

    clear:;
    for(int i = -n; i <= n; ++i)
        for(int j = -m; j <= m; ++j)
            cnt(i, j) = pre1(i, j) = pre2(i, j) = 0;
    for(int i = 1-lx; i <= n-lx; ++i)
        for(int j = 1-ly; j <= m-ly; ++j)
            res(i, j) = 0;
    return 0;
}

int T;
int main(){
    read(T);
    while(T--) _main();
    return 0;
}

N - Qoj7745 Trapping Rain Water

思考题目性质,发现前缀 max 和后缀 max 构成一个连续段,于是我们用珂朵莉维护这个东西即可。

\(\mathrm O(n \log n)\)。注意细节。

#include <bits/stdc++.h>
#define llong long long
#define N 100005
using namespace std;

#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
    x = 0; int w = 1;
    char ch = gc();
    while(ch < '0' || ch > '9'){
        if(ch == '-') w = -w;
        ch = gc();
    }
    while(ch >= '0' && ch <= '9')
        x = (x<<3)+(x<<1)+(ch^48), ch = gc();
    x *= w;
}
template<typename T, typename... Args>
inline void read(T& x, Args&... y){
    return read(x), read(y...);
}

int n, q;
llong a[N], maxn1[N], maxn2[N], pos;
llong s1, s2;

typedef tuple<int, int, llong> Node;
set<Node> odt1, odt2;

int _main(){
    read(n);
    for(int i = 1; i <= n; ++i){
        read(a[i]), s1 += a[i];
        if(a[i] > a[pos]) pos = i;
    }
    for(int i = 1; i <= n; ++i) maxn1[i] = max(maxn1[i-1], a[i]);
    for(int i = n; i >= 1; --i) maxn2[i] = max(maxn2[i+1], a[i]);
    for(int i = 1; i <  pos; ++i) odt1.emplace(i, i, maxn1[i]), s2 += maxn1[i];
    for(int i = pos; i <= n; ++i) odt2.emplace(i, i, maxn2[i]), s2 += maxn2[i];
    read(q);
    while(q--){
        int x, k; read(x, k);
        a[x] += k, s1 += k;
        if(x < pos){
            auto it1 = odt1.lower_bound({x+1, 0, 0}); --it1;
            if(a[x] <= get<2>(*it1)) goto output;
            int L = get<0>(*it1);
            llong w = get<2>(*it1);
            if(a[x] >= a[pos]){
                for(auto it = it1, tmp = it1; it != odt1.end(); ){
                    s2 -= (get<1>(*it)-get<0>(*it)+1)*get<2>(*it);
                    tmp = it++;
                    odt1.erase(tmp);
                }
                if(L != x) odt1.emplace(L, x-1, w), s2 += ((x-1)-L+1)*w;
                int R = get<1>(*odt2.begin());
                odt2.erase(odt2.begin()); s2 -= (R-pos+1)*a[pos];
                odt2.emplace(x, x, a[x]), s2 += a[x];
                odt2.emplace(x+1, R, a[pos]), s2 += (R-(x+1)+1)*a[pos];
                pos = x;
            }
            else{
                auto it2 = it1;
                while(it2 != odt1.end() && get<2>(*it2) <= a[x]) ++it2;
                --it2;
                int R = get<1>(*it2);
                for(auto it = it1, tmp = it1; it != it2; ){
                    s2 -= (get<1>(*it)-get<0>(*it)+1)*get<2>(*it);
                    tmp = it++;
                    odt1.erase(tmp);
                }
                s2 -= (get<1>(*it2)-get<0>(*it2)+1)*get<2>(*it2);
                odt1.erase(it2);
                if(L != x) odt1.emplace(L, x-1, w), s2 += ((x-1)-L+1)*w;
                odt1.emplace(x, R, a[x]), s2 += (R-x+1)*a[x];
            }
        }
        else if(x > pos){
            auto it1 = odt2.lower_bound({x+1, 0, 0}); --it1;
            if(a[x] <= get<2>(*it1)) goto output;
            int R = get<1>(*it1);
            llong w = get<2>(*it1);
            if(a[x] > a[pos]){
                for(auto it = odt2.begin(), tmp = it; it != it1; ){
                    s2 -= (get<1>(*it)-get<0>(*it)+1)*get<2>(*it);
                    tmp = it++;
                    odt2.erase(tmp);
                }
                s2 -= (get<1>(*it1)-get<0>(*it1)+1)*get<2>(*it1);
                odt2.erase(it1);
                if(R != x) odt2.emplace(x+1, R, w), s2 += (R-(x+1)+1)*w;
                odt1.emplace(pos, x-1, a[pos]), s2 += ((x-1)-pos+1)*a[pos];
                odt2.emplace(x, x, a[x]), s2 += a[x];
                pos = x;
            }
            else{
                auto it2 = it1;
                while(it2 != odt2.begin() && get<2>(*it2) <= a[x]) --it2;
                int L;
                if(get<2>(*it2) <= a[x]) L = get<0>(*it2);
                else                     L = get<1>(*it2)+1, ++it2;
                for(auto it = it2, tmp = it2; it != it1; ){
                    s2 -= (get<1>(*it)-get<0>(*it)+1)*get<2>(*it);
                    tmp = it++;
                    odt2.erase(tmp);
                }
                s2 -= (get<1>(*it1)-get<0>(*it1)+1)*get<2>(*it1);
                odt2.erase(it1);
                if(R != x) odt2.emplace(x+1, R, w), s2 += (R-(x+1)+1)*w;
                odt2.emplace(L, x, a[x]), s2 += (x-L+1)*a[x];
            }
        }
        else{
            int R = get<1>(*odt2.begin());
            llong w = get<2>(*odt2.begin());
            auto it1 = odt2.begin();
            s2 -= (get<1>(*it1)-get<0>(*it1)+1)*get<2>(*it1);
            odt2.erase(it1);
            if(R != x) odt2.emplace(x+1, R, w), s2 += (R-(x+1)+1)*w;
            odt2.emplace(x, x, a[x]), s2 += a[x];
        }
        output: printf("%lld\n", s2-s1);
    }
    odt1.clear(), odt2.clear();
    for(int i = 1; i <= n; ++i) maxn1[i] = maxn2[i] = 0;
    s1 = s2 = pos = 0;
    return 0;
}

int T;
int main(){
    read(T);
    while(T--) _main();
    return 0;
}

O - Qoj9488 Do Not Turn Back

不要回头!(

特判 \(k = 1\)

考虑回头的向量,发现是每一个节点的对应值乘上 \(deg_i - 1\)。于是构造转移矩阵 \(\begin{bmatrix} A & -C \\ I & 0 \end{bmatrix}\),其中 \(A\) 是邻接矩阵,\(C\) 是对角矩阵,其中 \(C_{i, i} = deg_i - 1\)。构造初始状态 \(\begin{bmatrix} f_1 \\ f_2 \end{bmatrix}\),注意不是 \(\begin{bmatrix} f_0 \\ f_1 \end{bmatrix}\)。然后矩阵快速幂跑一下即可。

时间复杂度 \(\mathrm O(n^3 \log k)\)

#include <bits/stdc++.h>
#define llong long long
#define N 205
using namespace std;

#define bs (1<<20)
char buf[bs], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,bs,stdin),p1==p2)?EOF:*p1++)
template<typename T>
inline void read(T& x){
    x = 0; int w = 1;
    char ch = gc();
    while(ch < '0' || ch > '9'){
        if(ch == '-') w = -w;
        ch = gc();
    }
    while(ch >= '0' && ch <= '9')
        x = (x<<3)+(x<<1)+(ch^48), ch = gc();
    x *= w;
}
template<typename T, typename ...Args>
inline void read(T& x, Args& ...y){
    return read(x), read(y...);
}

constexpr llong p = 998244353;

int n, m, k;
llong a[N][N], tmp1[N][N];
llong b[N], tmp2[N], deg[N];

inline void op1(){
    #ifdef DEBUG
    cerr << "Before op1:" << endl;
    for(int i = 1; i <= n*2; ++i) cerr << b[i] << " ";
    cerr << endl << endl;
    for(int i = 1; i <= n*2; ++i){
        for(int j = 1; j <= n*2; ++j)
            cerr << a[i][j] << " ";
        cerr << endl;
    }
    cerr << endl;
    #endif
    for(int i = 1; i <= n*2; ++i) tmp2[i] = 0;
    for(int i = 1; i <= n*2; ++i)
        for(int j = 1; j <= n*2; ++j)
            tmp2[i] = (tmp2[i]+a[i][j]*b[j])%p;
    for(int i = 1; i <= n*2; ++i) b[i] = tmp2[i];
    #ifdef DEBUG
    cerr << "After op1:" << endl;
    for(int i = 1; i <= n*2; ++i) cout << b[i] << " ";
    cerr << endl << endl;
    #endif
    return;
}
inline void op2(){
    #ifdef DEBUG
    cerr << "Before op2:" << endl;
    for(int i = 1; i <= n*2; ++i){
        for(int j = 1; j <= n*2; ++j)
            cerr << a[i][j] << " ";
        cerr << endl;
    }
    cerr << endl;
    #endif
    for(int i = 1; i <= n*2; ++i)
        for(int j = 1; j <= n*2; ++j) tmp1[i][j] = 0;
    for(int i = 1; i <= n*2; ++i)
        for(int j = 1; j <= n*2; ++j)
            for(int k = 1; k <= n*2; ++k)
                tmp1[i][j] = (tmp1[i][j]+a[i][k]*a[k][j])%p;
    for(int i = 1; i <= n*2; ++i)
        for(int j = 1; j <= n*2; ++j) a[i][j] = tmp1[i][j];
    #ifdef DEBUG
    cerr << "After op2:" << endl;
    for(int i = 1; i <= n*2; ++i){
        for(int j = 1; j <= n*2; ++j)
            cerr << a[i][j] << " ";
        cerr << endl;
    }
    cerr << endl;
    #endif
    return;
}

int main(){
    read(n, m, k);
    for(int i = 1; i <= m; ++i){
        int u, v; read(u, v);
        ++a[u][v], ++a[v][u];
        ++deg[u], ++deg[v];
    }
    if(k == 1){
        printf("%lld", a[1][n]);
        return 0;
    }
    k -= 2;
    for(int i = 1; i <= n; ++i)
        a[i+n][i] = 1, a[i][i+n] = (p-deg[i]+1)%p;
    for(int i = 1; i <= n; ++i) b[i+n] = a[1][i];
    for(int i = 1; i <= n; ++i)
        for(int j = 2; j <= n; ++j)
            b[j] = (b[j]+a[j][i]*b[i+n])%p;
    while(k){
        if(k & 1) op1();
        op2(), k >>= 1;
    }
    printf("%lld", b[n]);
    return 0;
}

posted @ 2026-05-12 10:04  Hootime  阅读(20)  评论(0)    收藏  举报