线段树分治

在OIwiki上的称呼是线段树与离线询问。

 

C. 地理课

乘除法可以直接在原有答案上操作,其他的就比较板子了。

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 3;
const int mod = 1e9 + 7;

typedef pair<int, int> pii;
map<pii, int> mp;
int ans[maxn], n, m, top;
ll inv[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

struct stac
{
    int s, f1, f2, id;//把f1合并到f2
}rem[maxn<<3];

struct SET
{
    int f[maxn], size[maxn];
    void pre()
    {
        for(int i=1; i<=n; i++) f[i] = i;
        for(int i=1; i<=n; i++) size[i] = 1;
    }
    int fa(int x)
    {
        return f[x] == x ? x : fa(f[x]);
    }
}s;

struct op 
{
    int op, x, y, r;
}o[maxn];

struct Tree 
{
    vector<int> v[maxn<<2];
    void modify(int x, int l, int r, int L, int R, int now)
    {
        if(L <= l && r <= R)
        {
            v[x].push_back(now);
            return;
        }
        int mid = (l + r) >> 1;
        if(L <= mid) modify(x<<1, l, mid, L, R, now);
        if(R > mid) modify(x<<1|1, mid+1, r, L, R, now);
    }
    ll nans = 1;
    void LINK(int x)
    {
        for(int i : v[x])
        {
            int u = o[i].x, v = o[i].y;
            u = s.fa(u), v = s.fa(v);
            if(u == v) continue;
            if(s.size[u] < s.size[v]) swap(u, v);
            nans = nans * inv[s.size[u]] % mod * inv[s.size[v]] % mod;
            rem[++top].f1 = v;
            rem[top].f2 = u;
            rem[top].s = s.size[v];
            rem[top].id = x;
            s.size[u] += s.size[v];
            s.size[v] = 0;
            nans = nans * s.size[u] % mod;
            s.f[v] = u;
        }
    }
    void CUT(int x)
    {
        while(rem[top].id == x)
        {
            stac &o = rem[top];
            if(o.id != x) return;
            s.f[o.f1] = o.f1;
            s.f[o.f2] = o.f2;
            nans = nans * inv[s.size[o.f2]] % mod;
            s.size[o.f2] -= o.s;
            s.size[o.f1] = o.s;
            nans = nans * s.size[o.f1] % mod * s.size[o.f2] % mod;
            --top;
        }
    }
    void work(int x, int l, int r)
    {
        LINK(x);
        if(l == r)
        {
            ans[l] = nans;
            CUT(x);
            return;
        }
        int mid = (l + r) >> 1;
        work(x<<1, l, mid);
        work(x<<1|1, mid+1, r);
        CUT(x);
    }
}t;

int main()
{
    n = read(); m = read();
    inv[1] = 1;
    for(int i=2; i<=n; i++)
    {
        inv[i] = (mod-mod/i)*inv[mod%i]%mod;
    }
    s.pre();
    for(int i=1; i<=m; i++)
    {
        o[i].op = read();
        o[i].x = read();
        o[i].y = read();
        if(o[i].x > o[i].y) swap(o[i].x, o[i].y);
        if(o[i].op == 2)
        {
            o[mp[pii(o[i].x, o[i].y)]].r = i - 1;
        }
        else mp[pii(o[i].x, o[i].y)] = i;
    }
    for(int i=1; i<=m; i++)
    {
        if(o[i].op == 1) t.modify(1, 1, m, i, o[i].r ? o[i].r : m, i);
    }
    t.work(1, 1, m);
    for(int i=1; i<=m; i++) printf("%d\n", ans[i]);

    return 0;
}

 

P5787 二分图 /【模板】线段树分治

用可撤销的东西判断二分图?那就是拓展域并查集!

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 10101010;
const int mod = 1e9 + 7;

int n, m, k, f[maxn], siz[maxn], top;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct E {int x, y;}e[maxn];

struct Stack 
{
    int x, y, add;
}st[maxn];

int fa(int x) {return f[x] == x ? x : fa(f[x]);}

void merge(int x, int y)
{
    x = fa(x), y = fa(y);
    if(siz[x] > siz[y]) swap(x, y);
    st[++top] = (Stack){x, y, siz[x] == siz[y]};
    f[x] = y;
    if(siz[x] == siz[y]) siz[y]++;
}

struct seg 
{
    vector<int> t[maxn];
    void update(int x, int l, int r, int L, int R, int id)
    {
        if(L <= l && r <= R)
        {
            t[x].push_back(id);
            return;
        }
        int mid = (l + r) >> 1;
        if(L <= mid) update(x<<1, l, mid, L, R, id);
        if(R > mid) update(x<<1|1, mid+1, r, L, R, id);
    }
    void solve(int x, int l, int r)
    {
        int ans = 1;
        int lasttop = top;
        for(int i : t[x])
        {
            int u = fa(e[i].x), v = fa(e[i].y);
            if(u == v)
            {
                for(int k=l; k<=r; k++) printf("No\n");
                ans = 0; break;
            }
            merge(e[i].x, e[i].y+n);
            merge(e[i].y, e[i].x+n);
        }
        if(ans)
        {
            if(l == r) printf("Yes\n");
            else 
            {
                int mid = (l + r) >> 1;
                solve(x<<1, l, mid);
                solve(x<<1|1, mid+1, r);
            }
        }
        //撤销操作不仅可以记录树的编号即点的来源,还可以记录top的初值
        while(top > lasttop)
        {
            siz[f[st[top].x]] -= st[top].add;
            f[st[top].x] = st[top].x;
            top--;
        }
        return;
    }
}t;

int main()
{
    n = read(); m = read(); k = read();
    for(int i=1; i<=m; i++)
    {
        e[i].x = read(); e[i].y = read();
        int l = read()+1, r = read();//防止下标为0,本来r应该-1的
        t.update(1, 1, k, l, r, i);
    }
    for(int i=1; i<=2*n; i++) f[i] = i, siz[i] = 1;
    t.solve(1, 1, k);

    return 0;
}

 

C. No Rest for the Wicked

https://www.cnblogs.com/Catherine2006/p/16785351.html CSP-S 模拟18

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 4e5 + 5;
const int inf = 1e9 + 7;

int n, m, ls[maxn+maxn], f[maxn], siz[maxn], val[maxn], ans[maxn];
bool vis[maxn];
vector<int> sol[maxn+maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int fa(int x) {return f[x] == x ? x : fa(f[x]);}

struct edge 
{
    int u, v;
}e[maxn];

struct city//为什么国家变成了城市。。
{
    int c, t, s, id;
}d[maxn];

struct sta
{
    bool dir;
    int fx, x, valfx, tim;
}st[maxn];
int top;

void add_edge(int tim, int id, bool dir)
{
    if(dir)
    {
        int u = e[id].u, v = e[id].v;
        u = fa(u), v = fa(v);
        if(val[u] < ans[v])
        {
            ++top; st[top].tim = tim;
            st[top].dir = dir;
            st[top].x = st[top].fx = u;
            st[top].valfx = val[u];
            val[u] = ans[v];
        }
    }
    else 
    {
        int u = e[id].u, v = e[id].v;
        u = fa(u), v = fa(v);
        if(u == v) return;
        if(siz[u] < siz[v]) swap(u, v);

        ++top;
        st[top].x = v;
        st[top].fx = u;
        st[top].valfx = val[u];
        st[top].dir = dir;
        st[top].tim = tim;

        f[v] = u;
        val[u] = max(val[u], val[v]);
        siz[u] += siz[v];
    }
}

void del(int tim)
{
    while(top && st[top].tim == tim)
    {
        if(st[top].dir)
        {
            val[st[top].x] = st[top].valfx;
        }
        else 
        {
            int u = st[top].fx, v = st[top].x;
            siz[u] -= siz[v];
            f[v] = v;
            val[u] = st[top].valfx;
        }
        --top;
    }
}

struct seg 
{
    struct node 
    {
        vector<int> dir, nir;
    }t[maxn<<2];
    void insert(int x, int l, int r, int L, int R, int id, bool dir)
    {
        if(L <= l && r <= R)
        {
            if(dir) t[x].dir.push_back(id);
            else t[x].nir.push_back(id);
            return;
        }
        int mid = (l + r) >> 1;
        if(L <= mid) insert(x<<1, l, mid, L, R, id, dir);
        if(R > mid) insert(x<<1|1, mid+1, r, L, R, id, dir);
    }
    void solve(int x, int l, int r)
    {
        for(int v : t[x].dir) add_edge(x, v, 1);
        for(int v : t[x].nir) add_edge(x, v, 0);
        if(l == r)
        {
            for(int v : sol[l]) ans[v] = max(ans[v], val[fa(v)]);
            del(x);
            return;
        }
        int mid = (l + r) >> 1;
        solve(x<<1|1, mid+1, r);
        solve(x<<1, l, mid);
        del(x);
    }
}t;

int main()
{
    n = read(); m = read();
    for(int i=1; i<=n; i++) d[i].c = read(), d[i].t = read(), d[i].s = read();
    for(int i=1; i<=m; i++) e[i].u = read(), e[i].v = read();
    int cnt = 0;
    for(int i=1; i<=n; i++) ls[++cnt] = d[i].c, ls[++cnt] = d[i].t;
    sort(ls+1, ls+cnt+1); cnt = unique(ls+1, ls+cnt+1)-ls-1;
    for(int i=1; i<=n; i++) d[i].c = lower_bound(ls+1, ls+cnt+1, d[i].c)-ls;
    for(int i=1; i<=n; i++) d[i].t = lower_bound(ls+1, ls+cnt+1, d[i].t)-ls;
    for(int i=1; i<=n; i++) sol[d[i].c].push_back(i);
    for(int i=1; i<=m; i++)
    {
        int &u = e[i].u, &v = e[i].v;
        if(d[u].c > d[v].c) swap(u, v);
        if(d[v].c <= min(d[v].t, d[u].t)) t.insert(1, 1, cnt, d[v].c, min(d[v].t, d[u].t), i, 0);
        if(d[u].c <= min(d[v].c-1, d[u].t)) t.insert(1, 1, cnt, d[u].c, min(d[v].c-1, d[u].t), i, 1);
    }
    for(int i=1; i<=n; i++) f[i] = i, siz[i] = 1, val[i] = d[i].s, ans[i] = d[i].s;
    t.solve(1, 1, cnt);
    for(int i=1; i<=n; i++) printf("%d ", ans[i]);

    return 0;
}

%%%Chen_jr

 

P5227 [AHOI2013]连通图

我本来以为把边放到树上的时候,可以每次删掉当前“时刻”把前后直接全都覆盖上,但是集合没有保证每条边只出现一次,它可以继续被删掉,所以不能在输入时加边。

还有就是我本来想循环一遍n判断它们所属的fa是不是同一个来判断连通图,事实上联通块的大小就是现成的联通依据!!

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 5;
const int inf = 1e9 + 7;

int ans[maxn], f[maxn], n, m, k, siz[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct edge 
{
    int u, v;
}e[maxn<<1];
vector<int> pos[maxn<<1];

vector<int> t[maxn<<2];

void insert(int x, int l, int r, int L, int R, int id)
{
    if(L <= l && r <= R)
    {
        t[x].push_back(id); return;
    }
    int mid = (l + r) >> 1;
    if(L <= mid) insert(x<<1, l, mid, L, R, id);
    if(R > mid) insert(x<<1|1, mid+1, r, L, R, id);
}

int fa(int x)
{
    while(f[x] != x) x = f[x];
    return x;
}

struct Stack 
{
    int tim, x, fx;
}st[maxn<<3];
int top;

bool add_edge(int x, bool flag)
{
    for(int i : t[x])
    {
        int u = e[i].u, v = e[i].v;
        u = fa(u), v = fa(v);
        if(u == v) continue;
        if(siz[u] < siz[v]) swap(u, v);
        top++;
        st[top].x = v; 
        st[top].fx = u; 
        st[top].tim = x;
        f[v] = u; 
        siz[u] += siz[v];
        if(siz[u] == n) flag = true;
    }
    return flag;
}

void del(int x)
{
    while(top && st[top].tim == x)
    {
        int u = st[top].fx, v = st[top].x;
        f[v] = v;
        siz[u] -= siz[v];
        top--;
    }
}

void solve(int x, int l, int r, bool flag)
{
    flag = add_edge(x, flag);
    if(l == r)
    {
        if(flag) printf("Connected\n");
        else printf("Disconnected\n");
        del(x);
        return;
    }
    int mid = (l + r) >> 1;
    solve(x<<1, l, mid, flag); 
    solve(x<<1|1, mid+1, r, flag);
    del(x);
}

int main()
{
    n = read(); m = read();
    for(int i=1; i<=m; i++)
    {
        e[i].u = read(); e[i].v = read();
    }
    k = read();
    for(int i=1; i<=m; i++)
    {
        pos[i].push_back(0);
    }
    for(int i=1; i<=k; i++)
    {
        for(int c=read(); c; c--)
        {
            pos[read()].push_back(i);
        }
    }
    for(int i=1; i<=m; i++)
    {
        pos[i].push_back(k+1);
    }
    for(int i=1; i<=m; i++)
    {
        for(int j=1; j<(int)pos[i].size(); j++)
        {
            if(pos[i][j-1]+1 <= pos[i][j]-1)
            {
                insert(1, 1, k, pos[i][j-1]+1, pos[i][j]-1, i);
            }
        }
    }
    for(int i=1; i<=n; i++) f[i] = i, siz[i] = 1;
    solve(1, 1, k, false);

    return 0;
}

 

P4585 [FJOI2015]火星商店问题

我怀疑我没看懂它的日期是怎么划分的是题面的问题!!每个0是新的一天,我本来以为一串0和一串1一组然后模半天样例模不出来。。

这题刷新了我对线段树分治的的认知,我本来以为它只是个动态图连通性和可撤销按秩合并并查集,结果这个题既没有图也没有撤销。。Trie树好像不能撤销,解决方案居然是新建一个。

关于随时间消失的变量,我本来一直以为是物品,可是d又不是定值,鹤完才知道消失的是问题。找到每一个查询对应的时间范围(对于这个询问来说合法的加入时间)在线段树上的区间,如果一个区间被这个问题完全包含就把问题的编号放进去。如果这个点上有这个询问的编号,这个点内所有的物品的时间都满足条件。

查询时的最后两个变量放的是在这个区间内新加入的物品的数量,把所有物品先按位置排序,在线段树上依据时间把物品再次分组,原来按位置排的顺序没有被打乱。每一个节点在刚刚被找到的时候这里面的物品位置是有序的,这样就可以二分找到边界。现在位置满足了条件。

关于区间异或最大值,方案是建一个可持久化01Trie。

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 3;
const int mod = 1e9 + 7;

int n, m, cnt1, cnt2, tot, top, rt[maxn], ans[maxn], st[maxn];
int ch[maxn*20][2], sz[maxn*20];
vector<int> a[maxn<<2];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

void insert(int &x, int u, int w)
{
    int now; now = x = ++tot;
    for(int i=17; i>=0; i--)
    {
        bool d = w & (1<<i);
        ch[now][d^1] = ch[u][d^1]; ch[now][d] = ++tot;
        now = ch[now][d]; u = ch[u][d];
        sz[now] = sz[u] + 1;
    }
}

int query(int l, int r, int w)
{
    int res = 0;
    for(int i=17; i>=0; i--)
    {
        bool d = w & (1<<i);
        if(sz[ch[r][d^1]]-sz[ch[l][d^1]]>0)
        {
            l = ch[l][d^1], r = ch[r][d^1]; res += (1<<i);
        }
        else l = ch[l][d], r = ch[r][d];
    }
    return res;
}

struct buy
{
    int s, v, t;
    bool operator < (const buy &T) const 
    {
        return s < T.s;
    }
}q[maxn], t1[maxn], t2[maxn];

struct guest 
{
    int l, r, L, R, x;
}p[maxn];

void update(int x, int l, int r, int L, int R, int id)
{
    if(L > R || r < L || l > R) return;
    if(L <= l && r <= R) {a[x].push_back(id); return;}
    int mid = (l + r) >> 1;
    update(x<<1, l, mid, L, R, id); update(x<<1|1, mid+1, r, L, R, id);
}

void calc(int x, int L, int R)
{
    top = tot = 0;
    for(int i=L; i<=R; i++)
    {
        st[++top] = q[i].s;
        insert(rt[top], rt[top-1], q[i].v);
    }
    for(int i=0,sz=a[x].size(); i<sz; i++)
    {
        int k = a[x][i];
        int l = upper_bound(st+1, st+1+top, p[k].l-1)-st-1;
        int r = upper_bound(st+1, st+1+top, p[k].r)-st-1;
        ans[k] = max(ans[k], query(rt[l], rt[r], p[k].x));
    }
}

void divide(int x, int l, int r, int L, int R)
{
    if(L > R) return;
    int cn1 = 0, cn2 = 0;
    calc(x, L, R);
    if(l == r) return;
    int mid = (l + r) >> 1;
    for(int i=L; i<=R; i++)
    {
        if(q[i].t <= mid) t1[++cn1] = q[i];
        else t2[++cn2] = q[i];
    }
    for(int i=1; i<=cn1; i++) q[i+L-1] = t1[i];
    for(int i=1; i<=cn2; i++) q[i+L-1+cn1] = t2[i];
    divide(x<<1, l, mid, L, L+cn1-1);
    divide(x<<1|1, mid+1, r, L+cn1, R);
}

int main()
{
    n = read(); m = read();
    for(int i=1; i<=n; i++) insert(rt[i], rt[i-1], read());
    for(int i=1; i<=m; i++)
    {
        int tp = read();
        if(!tp) {int s = read(), v = read(); q[++cnt1] = (buy){s, v, cnt1};}
        else 
        {
            int l = read(), r = read(), x = read(), d = read();
            ans[++cnt2] = query(rt[l-1], rt[r], x);
            p[cnt2] = (guest){l, r, max(1, cnt1-d+1), cnt1, x};
        }
    }
    for(int i=1; i<=cnt2; i++) update(1, 1, cnt1, p[i].L, p[i].R, i);
    sort(q+1, q+1+cnt1);
    divide(1, 1, cnt1, 1, cnt1);
    for(int i=1; i<=cnt2; i++) printf("%d\n", ans[i]);

    return 0;
}

 

P3733 [HAOI2017]八纵八横

我到现在才知道:原来线段树的区间下标可以从0开始!!

关于查询异或和最大的环,可以找到所有的环把它们插入线性基,找环的方法是先建一棵生成树,记录下来所有的非树边并按时间放到线段树上。建设方案不会拆掉已有的高速公路,只可能改变计划,所以生成树始终不变,图始终是连通图。

为了不进行线性基的删除,把当前线性基的状态直接传入函数,这样不需要del()直接return就是回溯。

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1200;
const int inf = 1e7;

#define bs bitset<1005>
int n, m, P, maxbit, tot, pos[maxn], tim;
bs dis[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct line 
{
    bs p[maxn];
    void insert(bs x)
    {
        for(int i=maxbit; i>=0; i--)
        {
            if(!x[i]) continue;
            if(!p[i].any()) {p[i] = x; break;}
            else x ^= p[i];
        }
    }
    bs query()
    {
        bs tmp;
        tmp.reset();
        for(int i=maxbit; i>=0; i--) if(!tmp[i]) tmp^=p[i];
        return tmp;
    }
}Ba;

void print(bs res)
{
    bool flag = 0;
    for(int i=maxbit; i>=0; i--)
    {
        if(!flag && res[i]) flag = 1;
        if(flag) putchar(res[i]+'0');
    }
    if(!flag) putchar('0');
    putchar('\n');
}

struct bcj 
{
    int f[maxn];
    void init()
    {
        for(int i=1; i<=n; i++) f[i] = i;
    }
    int fa(int x)
    {
        while(f[x] != x) x = f[x] = f[f[x]];
        return x;
    }
    void merge(int x, int y)
    {
        x = fa(x); y = fa(y);
        f[x] = y;
    }
}A;

struct node 
{
    int next, to;
    bs w;
}a[maxn<<1];
int head[maxn], len;

void add(int x, int y, bs w)
{
    a[++len].to = y; a[len].next = head[x]; a[len].w = w;
    head[x] = len;
}

void dfs(int u, int fa)
{
    for(int i=head[u]; i; i=a[i].next)
    {
        int v = a[i].to;
        if(v == fa) continue;
        dis[v] = dis[u]^a[i].w;
        dfs(v, u);
    }
}

struct Edge
{
    int u, v, l, r;
    bs w;
}E[maxn<<1];

struct seg 
{
    vector<int> t[maxn<<2];
    bs ans[maxn];
    void modify(int x, int l, int r, int L, int R, int id)
    {
        if(L <= l && r <= R)
        {
            t[x].push_back(id);
            return;
        }
        int mid = (l + r) >> 1;
        if(L <= mid) modify(x<<1, l, mid, L, R, id);
        if(R > mid) modify(x<<1|1, mid+1, r, L, R, id);
    }
    void query(int x, int l, int r, line tmp)
    {
        for(int i : t[x])
        {
            tmp.insert(dis[E[i].u]^dis[E[i].v]^E[i].w);
        }
        if(l == r) {ans[l] = tmp.query(); return;}
        int mid = (l + r) >> 1;
        query(x<<1, l, mid, tmp); query(x<<1|1, mid+1, r, tmp);
    }
}T;

int main()
{   
    n = read(); m = read(); P = read();
    A.init();
    for(int i=1; i<=m; i++)
    {
        int x = read(), y = read(); string st;
        cin >> st;
        maxbit = max(maxbit, (int)st.size());
        if(A.fa(x) != A.fa(y)) A.merge(x, y), add(x, y, bs(st)), add(y, x, bs(st));
        else E[++tot] = {x, y, 0, P, bs(st)};
    }
    dfs(1, 0);
    for(int i=1; i<=P; i++)
    {
        string opt, st;
        cin >> opt;
        if(opt[0] == 'A')
        {
            int x = read(), y = read(); cin >> st;
            maxbit = max(maxbit, (int)st.size());
            E[++tot] = {x, y, i, P, bs(st)}; pos[++tim] = tot;
        }
        else if(opt[1] == 'a')
        {
            int x = read();
            E[pos[x]].r = i-1;
        }
        else 
        {
            int x = read(); cin >> st;
            maxbit = max(maxbit, (int)st.size());
            E[pos[x]].r = i-1;
            E[++tot] = E[pos[x]]; E[tot].w = bs(st);
            E[tot].l = i; E[tot].r = P;
            pos[x] = tot;
        }
    }
    for(int i=1; i<=tot; i++)
    {
        T.modify(1, 0, P, E[i].l, E[i].r, i);
    }
    T.query(1, 0, P, Ba);
    print(T.ans[0]);
    for(int i=1; i<=P; i++) print(T.ans[i]);
    
    return 0;
}

 

CF576E Painting Edges

一条边一旦拥有颜色就不会再变回无色,所以加入的边都没有撤销,只是从上一次修改之后到下一次修改之前加入了很多次,颜色变化可以边查询边记录,pos一开始是询问的时间,后来变成了当前边的颜色,边的编号作为下标可以跨越线段树上的时间范围。

操作i能直接影响的范围是[i, j-1],它对染色操作[i+1, j-1]的影响取决于操作i能否被执行,于是就可以将操作i覆盖到[i+1, j-1]中,在分治到叶子节点时判断操作i能否被执行。操作i不需要覆盖自己在叶子上直接判断就好了。

交上去Waiting到现在了都没评出来,对不对我也不知道……

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 5e5 + 7;
const int K = 55;
const int inf = 1e7;

int pos[maxn], a[maxn], c[maxn], n, m, k, q, f[K][maxn<<1], siz[K][maxn<<1];
vector<int> t[maxn<<2];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct edge 
{
    int u, v;
}e[maxn];

struct Stack
{
    int tim, c, x, fx;
}st[maxn];
int top;

int find(int c, int x) {return f[c][x] == x ? x : find(c, f[c][x]);}

void merge(int c, int u, int v, int tim)
{
    if(u == v) return;
    if(siz[c][u] < siz[c][v]) swap(u, v);
    st[++top].c = c; 
    st[top].x = v; 
    st[top].fx = u;
    st[top].tim = tim;
    siz[c][u] += siz[c][v]; f[c][v] = u;
}

void del(int tim)
{
    while(st[top].tim == tim)
    {
        int v = st[top].x, u = st[top].fx, c = st[top].c;
        siz[c][u] -= siz[c][v];
        f[c][v] = v;
        top--;
    }
}

void update(int x, int l, int r, int L, int R, int id)
{
    if(L <= l && r <= R)
    {
        t[x].push_back(id); return;
    }
    int mid = (l + r) >> 1;
    if(L <= mid) update(x<<1, l, mid, L, R, id);
    if(R > mid) update(x<<1|1, mid+1, r, L, R, id);
}

void add_edge(int x)
{
    for(int i : t[x])
    {
        merge(c[i], find(c[i], e[a[i]].u), find(c[i], e[a[i]].v+n), x);
        merge(c[i], find(c[i], e[a[i]].u+n), find(c[i], e[a[i]].v), x);
    }
}

void solve(int x, int l, int r)
{
    add_edge(x);
    if(l == r)
    {
        if(find(c[l], e[a[l]].u) == find(c[l], e[a[l]].v)) puts("NO"), c[l] = pos[a[l]];
        else puts("YES"), pos[a[l]] = c[l];
        del(x);
        return;
    }
    int mid = (l + r) >> 1;
    solve(x<<1, l, mid); 
    solve(x<<1|1, mid+1, r);
    del(x);
}

int main()
{   
    n = read(); m = read(); k = read(); q = read();
    for(int j=1; j<=k; j++)
    {
        for(int i=1; i<=n; i++)
        {
            f[j][i] = i; f[j][i+n] = i+n;
            siz[j][i] = siz[j][i+n] = 1;
        }
    }
    for(int i=1; i<=m; i++)
    {
        e[i].u = read(); e[i].v = read(); pos[i] = q+1;
    }
    for(int i=1; i<=q; i++)
    {
        a[i] = read(); c[i] = read();
    }
    for(int i=q; i>=0; i--)
    {
        if(i+1<pos[a[i]]) update(1, 1, q, i+1, pos[a[i]]-1, i);
        pos[a[i]] = i;
    }
    memset(pos, 0, sizeof(pos));
    solve(1, 1, q);
    
    return 0;
}

 

P5214 [SHOI2014]神奇化合物

非常的模板,我差点就切了。。交上去先爆个0才发现map映射边的时候居然没有交换大小***

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 5;

typedef pair<int, int> pii;
int n, m, Q, f[5004], siz[5004];
bool vis[maxn];
map<pii, int> mp;
char op[2];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct Stack 
{
    int x, fx, tim;
}st[maxn<<2];
int top;

struct edge 
{
    int u, v, l, r;
}e[maxn];

int fa(int x) {return f[x] == x ? x : fa(f[x]);}

struct seg 
{
    vector<int> t[maxn<<2];
    int res;
    void update(int x, int l, int r, int L, int R, int id)
    {
        if(L <= l && r <= R)
        {
            t[x].push_back(id); return;
        }
        int mid = (l + r) >> 1;
        if(L <= mid) update(x<<1, l, mid, L, R, id);
        if(R > mid) update(x<<1|1, mid+1, r, L, R, id);
    }
    void del(int x)
    {
        while(st[top].tim == x)
        {
            int v = st[top].x, u = st[top].fx;
            siz[u] -= siz[v];
            f[v] = v;
            res++; top--;
        }
    }
    void add_edge(int x)
    {
        for(int i : t[x])
        {
            int u = fa(e[i].u), v = fa(e[i].v);
            if(u == v) continue;
            if(siz[u] < siz[v]) swap(u, v);
            st[++top].x = v; st[top].fx = u;
            st[top].tim = x;
            siz[u] += siz[v];
            f[v] = u; res--;
        }
    }
    void solve(int x, int l, int r)
    {
        add_edge(x);
        if(l == r)
        {
            if(vis[l]) printf("%d\n", res);
            del(x); return;
        }
        int mid = (l + r) >> 1;
        solve(x<<1, l, mid); solve(x<<1|1, mid+1, r);
        del(x);
    }
}t;

int main()
{
    n = read(); m = read();
    for(int i=1; i<=m; i++)
    {
        e[i].u = read(); e[i].v = read();
        if(e[i].u > e[i].v) swap(e[i].u, e[i].v);
        mp[pii(e[i].u, e[i].v)] = i;
        e[i].l = 1;
    }
    Q = read(); Q++;
    for(int i=2; i<=Q; i++)
    {
        scanf("%s", op);
        if(op[0] == 'Q') vis[i] = 1;
        else if(op[0] == 'D')
        {
            int u = read(), v = read();
            if(u > v) swap(u, v);
            int id = mp[pii(u, v)];
            e[id].r = i-1;
        }
        else 
        {
            e[++m].u = read(); e[m].v = read();
            if(e[m].u > e[m].v) swap(e[m].u, e[m].v);
            mp[pii(e[m].u, e[m].v)] = m;
            e[m].l = i;
        }
    }
    for(int i=1; i<=m; i++)
    {
        if(!e[i].r) e[i].r = Q;
        t.update(1, 1, Q, e[i].l, e[i].r, i);
    }
    for(int i=1; i<=n; i++) f[i] = i, siz[i] = 1;
    t.res = n;
    t.solve(1, 1, Q);

    return 0;
}

 

P4632 [APIO2018] 新家

如果你在做线段树分治的相关习题,建议不要点开这个题,因为它不是线段树分治而是线段树上二分,并且它是一个完完全全的鬼畜!!我至今不理解为什么离散化还不能去重,并且对线段树二分的过程表示十分的疑惑,总之就是我太菜了不配做这道题!!!

还有#define mid (l+r)/2 可以但是#define mid (l+r)>>1 就会RE!?我不知道为什么左端点莫名其妙的变成了0。。。

因为用define要加括号!!!!!包括对数组进行定义的时候,因为是直接替换,如果+几没有被括住并且在数组下标的时候还*2了,那最后相当于开的是加的那个数*2!!!!啊这为这玩意儿问了趟老师尴尬坏了。。。

来日方长吧这题我肯定得再做一遍。。直接鹤的连线段树都不想改了。。

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 2;
const double eps = 1e-6;

#define mid (l+r)/2
#define inf 999999999
int n, K, Q, m, pb, dic[maxn], pdic, seg[maxn<<4], aans[maxn], nu[maxn];
multiset<int> se[300001];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct eve 
{
    int x, k, l, r;
}a[300001];

struct que 
{
    int x, t, bh;
}q0[300001];

struct thi 
{
    int ty, nx, t;
    bool operator < (const thi &T) const 
    {
        if(t == T.t) return ty < T.ty;
        return t < T.t;
    }
}b[maxn];

void putin()
{
    n = read(); K = read(); Q = read();
    for(int i=1; i<=n; i++) a[i].x = read(), a[i].k = read(), a[i].l = read(), a[i].r = read();
    for(int i=1; i<=Q; i++) q0[i].x = read(), q0[i].t = read(), q0[i].bh = i;
}

void caldic()
{
    for(int i=1; i<=n; i++) dic[++pdic] = a[i].x, b[++pb] = (thi){1, i, a[i].l}, b[++pb] = (thi){1, i, a[i].r+1};
    for(int i=1; i<=Q; i++) dic[++pdic] = q0[i].x, b[++pb] = (thi){2, i, q0[i].t};
    sort(dic+1, dic+pdic+1);
    m = pdic + K; sort(b+1, b+pb+1);
    for(int i=1; i<=n; i++)
    {
        int np = lower_bound(dic+1, dic+pdic+1, a[i].x)-dic;
        a[i].x = np+(nu[np]++);
    }
    for(int i=1; i<=Q; i++)
    {
        int np = lower_bound(dic+1, dic+pdic+1, q0[i].x)-dic;
        q0[i].x = np+(nu[np]++);
    }
}

void build(int x, int l, int r)
{
    if(l == r)
    {
        if(l > pdic) seg[x] = -1;
        else seg[x] = inf;
    }
    else 
    {
        build(x<<1, l, mid);
        build(x<<1|1, mid+1, r);
        seg[x] = min(seg[x<<1], seg[x<<1|1]);
    }
}

void update(int x, int l, int r, int np, int nx)
{
    if(l == r) seg[x] = nx;
    else 
    {
        if(np <= mid) update(x<<1, l, mid, np, nx);
        else update(x<<1|1, mid+1, r, np, nx);
        seg[x] = min(seg[x<<1], seg[x<<1|1]);
    }
}

int query(int x, int l, int r, int L, int R)
{
    if(l > R || r < L) return inf;
    else if(L <= l && r <= R) return seg[x];
    else return min(query(x<<1, l, mid, L, R), query(x<<1|1, mid+1, r, L, R));
}

int segef(int x, int l, int r, int nx, int nm)
{
    if(l == r) return max(dic[l]-nx, nx-dic[nm]);
    int ng = min(nm, seg[x<<1|1]);
    if(ng!=-1&&(ng==inf||dic[ng]>=max(0,nx-(dic[mid+1]-1-nx)))) return segef(x<<1, l, mid, nx, ng);
    else return segef(x<<1|1, mid+1, r, nx, nm);
}

int main()
{
    putin();
    caldic();
    build(1, 1, m);
    for(int i=1; i<=K; i++) se[i].insert(pdic+i), dic[pdic+i] = inf;
    for(int i=1; i<=pb; i++)
    {
        if(b[i].ty == 1)
        {
            int ni = b[i].nx;
            if(b[i].t > a[ni].r) se[a[ni].k].erase(se[a[ni].k].find(a[ni].x));
            multiset<int>::iterator ii = se[a[ni].k].lower_bound(a[ni].x);
            int np, nq;
            np = *ii;
            if(ii != se[a[ni].k].begin()) nq = *(--ii); else nq = -1;
            if(b[i].t > a[ni].r) update(1, 1, m, np, nq), update(1, 1, m, a[ni].x, inf);
            else update(1, 1, m, np, a[ni].x), update(1, 1, m, a[ni].x, nq), se[a[ni].k].insert(a[ni].x);
        }
        else 
        {
            if(query(1, 1, m, pdic+1, m) == -1) aans[q0[b[i].nx].bh] = -1;
            else aans[q0[b[i].nx].bh] = segef(1, 1, m, dic[q0[b[i].nx].x], inf);
        }
    }
    for(int i=1; i<=Q; i++) printf("%d\n", aans[i]);

    return 0;
}
posted @ 2022-10-13 20:31  Catherine_leah  阅读(43)  评论(0编辑  收藏  举报
/* */