zoukankan      html  css  js  c++  java
  • uestc summer training #3 线段树优化建边

    线段树建边

    struct E
    {
            int value, modvalue;
    } a[MAXN << 3];
    pair<int, int> b[MAXN];
    int root, l[MAXN << 3], r[MAXN << 3], tot;
    int build(int a, int b)
    {
            int x;
            if (a == b)
            {
                    x =::b[a].second;
            }
            else
            {
                    x = ++tot;
            }
            if (a == b)
            {
                    return x;
            }
            int mid = (a + b) >> 1;
            l[x] = build(a, mid);
            r[x] = build(mid + 1, b);
            addedge(x, l[x]);
            addedge(x, r[x]);
            return x;
    }
    void ins(int x, int a, int b, int c, int d, int p)
    {
            if (d < c)
            {
                    return ;
            }
            if (c <= a && b <= d)
            {
                    addedge(p, x);
                    return;
            }
            int mid = (a + b) >> 1;
            if (c <= mid)
            {
                    ins(l[x], a, mid, c, d, p);
            }
            if (d > mid)
            {
                    ins(r[x], mid + 1, b, c, d, p);
            }
    }

    A

    如果把边数缩小到n^2可以接受的话 就是一个最小点基的裸题

    但是这里可能有n^2条边所以我们需要线段树优化建边 然后再求出SCC

    扣掉不包含原始n个节点的SCC或者把除叶子节点外线段树上的点权设为inf 然后跑最小点基

    claris姐姐版SCC缩点:

    #include<cstdio>
    #include<algorithm>
    #include<set>
    using namespace std;
    typedef pair<int, int> P;
    const int N = 1000010, M = 8000000;
    int n, m, i, j, x, y;
    long long ans;
    struct E
    {
            int p, r, c;
    } a[N];
    P b[N];
    int root, l[N], r[N], tot;
    set<P>T[N];
    int g[2][N], nxt[2][M], v[2][M], ed, f[N], q[N], t, vis[N], ban[N];
    inline void add(int x, int y)
    {
            v[0][++ed] = y;
            nxt[0][ed] = g[0][x];
            g[0][x] = ed;
            v[1][ed] = x;
            nxt[1][ed] = g[1][y];
            g[1][y] = ed;
    }
    inline void ADD(int x, int y)
    {
            v[1][++ed] = y;
            nxt[1][ed] = g[1][x];
            g[1][x] = ed;
    }
    int build(int a, int b)
    {
            int x;
            if (a == b)  //如果该点是叶子节点的话 值就为下标
            {
                    x =::b[a].second;
            }
            else //否则的话 就给该节点一个标号
            {
                    x = ++tot;
            }
            if (a == b)
            {
                    return x;
            }
            int mid = (a + b) >> 1;
            l[x] = build(a, mid);
            r[x] = build(mid + 1, b);
            add(x, l[x]);
            add(x, r[x]);
            return x;
    }
    void ins(int x, int a, int b, int c, int d, int p)
    {
            if (c <= a && b <= d)
            {
                    add(p, x);  //p是不会变的 如果满足条件的话就把p和x节点连上一条边
                    return;
            }
            int mid = (a + b) >> 1;
            if (c <= mid)
            {
                    ins(l[x], a, mid, c, d, p);
            }
            if (d > mid)
            {
                    ins(r[x], mid + 1, b, c, d, p);
            }
    }
    inline int askl(int x) //min >=x
    {
            int l = 1, r = n, mid, t;
            while (l <= r)
            {
                    mid = (l + r) >> 1;
                    if (b[mid].first >= x)
                    {
                            r = (t = mid) - 1;
                    }
                    else
                    {
                            l = mid + 1;
                    }
            }
            return t;
    }
    inline int askr(int x) //max <=x
    {
            int l = 1, r = n, mid, t;
            while (l <= r)
            {
                    mid = (l + r) >> 1;
                    if (b[mid].first <= x)
                    {
                            l = (t = mid) + 1;
                    }
                    else
                    {
                            r = mid - 1;
                    }
            }
            return t;
    }
    void dfs1(int x)
    {
            vis[x] = 1;
            for (int i = g[0][x]; i; i = nxt[0][i])
                    if (!vis[v[0][i]])
                    {
                            dfs1(v[0][i]);
                    }
            q[++t] = x;
    }
    void dfs2(int x, int y)
    {
            vis[x] = 0;
            f[x] = y;
            for (int i = g[1][x]; i; i = nxt[1][i])
                    if (vis[v[1][i]])
                    {
                            dfs2(v[1][i], y);
                    }
    }
    void dfs3(int x)
    {
            if (ban[x])
            {
                    return;
            }
            ban[x] = 1;
            for (int i = g[1][x]; i; i = nxt[1][i])
            {
                    dfs3(v[1][i]);
            }
    }
    inline void solve(int x)
    {
            if (vis[x])
            {
                    return;
            }
            vis[x] = 1;
            for (int i = g[1][x]; i; i = nxt[1][i])
            {
                    dfs3(v[1][i]);
            }
    }
    int main()
    {
            scanf("%d%d", &n, &m);
            for (i = 1; i <= n; i++)
            {
                    scanf("%d%d%d", &a[i].p, &a[i].r, &a[i].c);
                    b[i] = P(a[i].p, i);
            }
            sort(b + 1, b + n + 1); //根据每个点的位置进行排序
            tot = n; //初始会有n个节点
            root = build(1, n);  //建立线段树并对线段树上的节点进行赋值
            for (i = 1; i <= n; i++)
            {
                    int l = askl(a[i].p - a[i].r); //二分得到最左边炸到的节点
                    int r = askr(a[i].p + a[i].r); //二分得到最右边炸到的节点
                    ins(root, 1, n, l, r, i); //把该节点和线段树上范围为子区间的节点连一条边
            }
            for (t = 0, i = 1; i <= tot; i++)
                    if (!vis[i])
                    {
                            dfs1(i);
                    }
            for (i = tot; i; i--)
                    if (vis[q[i]])
                    {
                            dfs2(q[i], q[i]);
                    }
            ed = 0;  //ed为SCC的边总数
            for (i = 1; i <= tot; i++)  //SCC前向星初始化head数组
            {
                    g[1][i] = 0;
            }
            for (i = 1; i <= tot; i++)
                    for (j = g[0][i]; j; j = nxt[0][j])
                            if (f[i] != f[v[0][j]]) //不同SCC之间建边
                            {
                                    ADD(f[i], f[v[0][j]]);
                            }
            for (i = 1; i <= n; i++)
            {
                    solve(f[i]);
            }
            for (i = 1; i <= n; i++)
                    if (!ban[f[i]]) //如果f[i]这个SCC是合法的话 就插入该点的一个值
                    {
                            T[f[i]].insert(P(a[i].c, i));
                    }
            for (i = 1; i <= tot; i++)
                    if (!ban[i] && f[i] == i) //如果这个SCC合法且这个SCC的入度是0的话 就把这个SCC内最小的点权值加上
                    {
                            ans += T[i].begin()->first;
                    }
            while (m--)
            {
                    scanf("%d%d", &x, &y);
                    if (!ban[f[x]])  //
                    {
                            ans -= T[f[x]].begin()->first;
                            T[f[x]].erase(P(a[x].c, x));
                            T[f[x]].insert(P(a[x].c = y, x));
                            ans += T[f[x]].begin()->first;
                    }
                    printf("%lld
    ", ans);
            }
    }
    View Code

    自己写的:

    /*Huyyt*/
    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int mod = 1e9 + 7;
    const int gakki = 5 + 2 + 1 + 19880611 + 1e9;
    const int MAXN = 5e5 + 5, MAXM = 3e6 + 5;
    int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], ed = 1;
    typedef pair<int, int> P;
    const int N = 500010, M = 6000000;
    int n, m, i, j, x, y;
    long long ans;
    struct E
    {
            int p, r, c;
    } a[N];
    P b[N];
    int to2[MAXM << 1], nxt2[MAXM << 1], Head2[MAXN];
    int ed2 = 1;
    int root, l[N], r[N], tot;
    //set<P> T[N];
    int t;
    int value[N];
    set<P> valuemin[N];
    inline void addedge(int u, int v)
    {
            to[++ed] = v;
            nxt[ed] = Head[u];
            Head[u] = ed;
    }
    inline void addedge2(int u, int v)
    {
            to2[++ed2] = v;
            nxt2[ed2] = Head2[u];
            Head2[u] = ed2;
    }
    //inline void add(int x, int y)
    //{
    //        v[0][++ed] = y;
    //        nxt[0][ed] = g[0][x];
    //        g[0][x] = ed;
    //        v[1][ed] = x;
    //        nxt[1][ed] = g[1][y];
    //        g[1][y] = ed;
    //}
    //inline void ADD(int x, int y)
    //{
    //        v[1][++ed] = y;
    //        nxt[1][ed] = g[1][x];
    //        g[1][x] = ed;
    //}
    int build(int a, int b)
    {
            int x;
            if (a == b)
            {
                    x =::b[a].second;
            }
            else
            {
                    x = ++tot;
            }
            if (a == b)
            {
                    return x;
            }
            int mid = (a + b) >> 1;
            l[x] = build(a, mid);
            r[x] = build(mid + 1, b);
            addedge(x, l[x]);
            addedge(x, r[x]);
            return x;
    }
    void ins(int x, int a, int b, int c, int d, int p)
    {
            if (c <= a && b <= d)
            {
                    addedge(p, x);
                    return;
            }
            int mid = (a + b) >> 1;
            if (c <= mid)
            {
                    ins(l[x], a, mid, c, d, p);
            }
            if (d > mid)
            {
                    ins(r[x], mid + 1, b, c, d, p);
            }
    }
    inline int askl(int x) //min >=x
    {
            int l = 1, r = n, mid, t;
            while (l <= r)
            {
                    mid = (l + r) >> 1;
                    if (b[mid].first >= x)
                    {
                            r = (t = mid) - 1;
                    }
                    else
                    {
                            l = mid + 1;
                    }
            }
            return t;
    }
    inline int askr(int x) //max <=x
    {
            int l = 1, r = n, mid, t;
            while (l <= r)
            {
                    mid = (l + r) >> 1;
                    if (b[mid].first <= x)
                    {
                            l = (t = mid) + 1;
                    }
                    else
                    {
                            r = mid - 1;
                    }
            }
            return t;
    }
    int dfn[MAXN];//表示这个点在dfs的时候是第几个搜到的;
    int low[MAXN];//表示这个点及其子节点连的所有点里dfn的最小值
    int sta[MAXN];//存着当前所有可能能构成强连通分量的点
    int visit[MAXN];//表示一个点目前是否在sta中
    int color[MAXN];//表示一个点属于哪个强连通分量
    int deep;/*表示从前有多少个点被搜到*/
    int top;/*sta目前的大小*/
    int colorsum = 0;/*目前强连通分量的数目*/
    int rudu[MAXN];
    int ok[N];
    queue<int> q, anser;
    void tarjan(int x)
    {
            dfn[x] = ++deep;
            low[x] = deep;
            visit[x] = 1;
            sta[++top] = x;
            for (int i = Head[x]; i; i = nxt[i])
            {
                    int v = to[i];
                    if (!dfn[v])
                    {
                            tarjan(v);
                            low[x] = min(low[x], low[v]);
                    }
                    else
                    {
                            if (visit[v])
                            {
                                    low[x] = min(low[x], low[v]);
                            }
                    }
            }
            if (dfn[x] == low[x])
            {
                    color[x] = ++colorsum;
                    visit[x] = 0;
                    while (sta[top] != x)
                    {
                            color[sta[top]] = colorsum;
                            visit[sta[top--]] = 0;
                    }
                    top--;
            }
    }
    inline void read(int &v)
    {
            v = 0;
            char c = 0;
            int p = 1;
            while (c < '0' || c > '9')
            {
                    if (c == '-')
                    {
                            p = -1;
                    }
                    c = getchar();
            }
            while (c >= '0' && c <= '9')
            {
                    v = (v << 3) + (v << 1) + c - '0';
                    c = getchar();
            }
            v *= p;
    }
    int main()
    {
            read(n), read(m);
            for (i = 1; i <= n; i++)
            {
                    read(a[i].p), read(a[i].r), read(a[i].c);
                    b[i] = P(a[i].p, i);
            }
            sort(b + 1, b + n + 1);
            tot = n;
            root = build(1, n);
            for (i = 1; i <= n; i++)
            {
                    int l = askl(a[i].p - a[i].r);
                    int r = askr(a[i].p + a[i].r);
                    ins(root, 1, n, l, r, i);
            }
            for (int i = 1; i <= tot; i++)
            {
                    if (i <= n)
                    {
                            value[i] = a[i].c;
                    }
                    else
                    {
                            value[i] = INT_MAX;
                    }
            }
            for (int i = 1; i <= tot; i++)
            {
                    if (!dfn[i])
                    {
                            tarjan(i);
                    }
            }
            for (int i = 1; i <= tot; i++)
            {
                    valuemin[color[i]].insert(make_pair(value[i], i));
                    for (int j = Head[i]; j; j = nxt[j])
                    {
                            int v = to[j];
                            if (color[v] != color[i])
                            {
                                    rudu[color[v]]++;
                                    addedge2(color[i], color[v]);
                            }
                    }
            }
            for (int i = 1; i <= colorsum; i++)
            {
                    if (rudu[i] == 0)
                    {
                            q.push(i);
                    }
            }
            while (!q.empty())
            {
                    int now = q.front();
                    q.pop();
                    if (valuemin[now].begin()->first != INT_MAX)
                    {
                            anser.push(now);
                    }
                    else
                    {
                            for (int i = Head2[now]; i; i = nxt2[i])
                            {
                                    int v = to2[i];
                                    rudu[v]--;
                                    if (rudu[v] == 0)
                                    {
                                            q.push(v);
                                    }
                            }
                    }
            }
            while (!anser.empty())
            {
                    int now = anser.front();
                    anser.pop();
                    ans += valuemin[now].begin()->first;
                    ok[now] = 1;
                    //cout << now << "!!!" << endl;
            }
            int mi, ci;
            while (m--)
            {
                    read(mi), read(ci);
                    if (ok[color[mi]])
                    {
                            ans -= 1LL * valuemin[color[mi]].begin()->first;
                            valuemin[color[mi]].erase(make_pair(a[mi].c, mi));
                            a[mi].c = ci;
                            valuemin[color[mi]].insert(make_pair(a[mi].c, mi));
                            ans += 1LL * valuemin[color[mi]].begin()->first;
                    }
                    cout << ans << endl;
            }
    }
    View Code

    牛客第四场 J

    先线段树优化建边 然后把整个图扣下来拓扑排序 要判两种-1的情况 

    第一种是insert的时候区间里有-1 用前缀和来判

    第二种是拓扑排序的时候有环 用最终答案的size来判

    /*Huyyt*/
    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int mod = 1e9 + 7;
    const int gakki = 5 + 2 + 1 + 19880611 + 1e9;
    const int MAXN = 8e6 + 5, MAXM = 1e7 + 5;
    int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], ed = 1;
    typedef pair<int, int> P;
    const int N = 1000010, M = 10000000;
    int n, m, i, j, x, y;
    long long ans;
    struct E
    {
            int value, modvalue;
    } a[N];
    P b[N];
    int pre[N], flag = 1;
    int number = 0;
    int wait[N];
    int root, l[N], r[N], tot;
    queue<int> finalans;
    bool visit[N];
    int du[N];
    int sum = 0;
    int nowmod;
    int nowvalue[N];
    int t;
    priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
    inline void addedge(int u, int v)
    {
            du[v]++;
            to[++ed] = v;
            nxt[ed] = Head[u];
            Head[u] = ed;
    }
    int build(int a, int b)
    {
            int x;
            if (a == b)
            {
                    x =::b[a].second;
            }
            else
            {
                    x = ++tot;
            }
            if (a == b)
            {
                    return x;
            }
            int mid = (a + b) >> 1;
            l[x] = build(a, mid);
            r[x] = build(mid + 1, b);
            addedge(l[x], x);
            addedge(r[x], x);
            return x;
    }
    void ins(int x, int a, int b, int c, int d, int p)
    {
            if (d < c)
            {
                    return ;
            }
            if (c <= a && b <= d)
            {
                    //cout << "add " << x << " " << p << " l r " << l[x] << " " << r[x] << endl;
                    addedge(x, p);
                    return;
            }
            int mid = (a + b) >> 1;
            if (c <= mid)
            {
                    ins(l[x], a, mid, c, d, p);
            }
            if (d > mid)
            {
                    ins(r[x], mid + 1, b, c, d, p);
            }
    }
    inline void read(int &v)
    {
            v = 0;
            char c = 0;
            int p = 1;
            while (c < '0' || c > '9')
            {
                    if (c == '-')
                    {
                            p = -1;
                    }
                    c = getchar();
            }
            while (c >= '0' && c <= '9')
            {
                    v = (v << 3) + (v << 1) + c - '0';
                    c = getchar();
            }
            v *= p;
    }
    void get_du(int x)
    {
            //wait[++number] = x;
            visit[x] = true;
            for (int v, i = Head[x]; i; i = nxt[i])
            {
                    v = to[i];
                    du[v]++;
                    if (!visit[v])
                    {
                            get_du(v);
                    }
            }
    }
    void init(int x)
    {
            pre[0] = number = 0;
            sum = 0;
            flag = ed = 1;
            for (int i = 0; i <= x + 1; i++)
            {
                    visit[i] = Head[i] = du[i] = 0;
            }
    }
    int main()
    {
            int T;
            read(T);
            while (T--)
            {
                    read(n);
                    for (i = 1; i <= n; i++)
                    {
                            pre[i] = pre[i - 1];
                            read(a[i].value);
                            if (a[i].value != -1)
                            {
                                    sum++;
                            }
                            else
                            {
                                    pre[i]++;
                            }
                            a[i].modvalue = i - 1;
                            b[i] = P(a[i].modvalue, i);
                    }
                    tot = n;
                    if (sum == 0)
                    {
                            cout << endl;
                            init(tot);
                            continue;
                    }
                    root = build(1, n);
                    for (i = 1; i <= n; i++)
                    {
                            if (a[i].value != -1)
                            {
                                    nowmod = a[i].value % n;
                                    if (a[i].modvalue > nowmod)
                                    {
                                            //printf("ins %d %d %d
    ", i, nowmod + 1, a[i].modvalue);
                                            if (a[i].modvalue >= nowmod + 1 && pre[a[i].modvalue] - pre[nowmod] != 0)
                                            {
                                                    flag = 0;
                                                    break;
                                            }
                                            ins(root, 1, n, nowmod + 1, a[i].modvalue, i);
                                    }
                                    else if (a[i].modvalue < nowmod)
                                    {
                                            //printf("ins %d %d %d
    ", i, 1, a[i].modvalue);
                                            //printf("ins %d %d %d
    ", i, nowmod + 1, n);
                                            if (a[i].modvalue >= 1 && pre[a[i].modvalue] - pre[0] != 0)
                                            {
                                                    flag = 0;
                                                    break;
                                            }
                                            if (n >= nowmod + 1 && pre[n] - pre[nowmod] != 0)
                                            {
                                                    flag = 0;
                                                    break;
                                            }
                                            ins(root, 1, n, 1, a[i].modvalue, i);
                                            ins(root, 1, n, nowmod + 1, n, i);
                                    }
                            }
                    }
                    if (!flag)
                    {
                            cout << -1 << endl;
                            init(tot);
                            continue;
                    }
                    //cout << "!!!" << endl;
                    for (int i = 1; i <= tot; i++)
                    {
                            if (i <= n)
                            {
                                    nowvalue[i] = a[i].value;
                            }
                            else
                            {
                                    nowvalue[i] = -1;
                            }
                    }
                    //                for (int i = 1; i <= tot; i++)
                    //                {
                    //                        if (!visit[i])
                    //                        {
                    //                                get_du(i);
                    //                        }
                    //                }
                    //                for (int i = 1; i <= n; i++)
                    //                {
                    //                        cout << "i  du " << i << " " << du[i] << endl;
                    //                }
                    for (int i = 1; i <= tot; i++)
                    {
                            if (du[i] == 0)
                            {
                                    //cout << "push " << nowvalue[i] << "  " << i << endl;
                                    q.push(make_pair(nowvalue[i], i));
                            }
                    }
                    //cout << "!!!" << endl;
                    pair<int, int> cnt;
                    while (!q.empty())
                    {
                            cnt = q.top();
                            q.pop();
                            if (cnt.first >= 0)
                            {
                                    //cout << cnt.first << "zzzz" << endl;
                                    finalans.push(cnt.first);
                            }
                            for (int v, i = Head[cnt.second]; i; i = nxt[i])
                            {
                                    v = to[i];
                                    //cout << i << " too " << v << "   " << nowvalue[cnt.second] << " nowvalue " << nowvalue[v] << endl;
                                    du[v]--;
                                    if (du[v] == 0)
                                    {
                                            q.push(make_pair(nowvalue[v], v));
                                    }
                            }
                    }
                    if (finalans.size() != sum)
                    {
                            printf("-1
    ");
                    }
                    else
                    {
                            while (!finalans.empty())
                            {
                                    printf("%d", finalans.front());
                                    finalans.pop();
                                    if (finalans.size() > 0)
                                    {
                                            putchar(' ');
                                    }
                                    else
                                    {
                                            putchar('
    ');
                                    }
                            }
                    }
                    init(tot);
            }
    }
    View Code
  • 相关阅读:
    Physics Experiment POJ3684
    FLIPTILE POJ NO.3279
    [蓝桥杯][算法提高VIP]盾神与积木游戏
    棋盘问题
    出栈次数的DFS计算
    Bribe the Prisoners
    Crazy Rows
    关于部分“记录”密码获取^o^/
    NOIP2017爆炸记
    【luogu 1024 一元三次方程求解】二分思想
  • 原文地址:https://www.cnblogs.com/Aragaki/p/9332865.html
Copyright © 2011-2022 走看看