• 省选测试1


    省选测试1

    T1

    ​ 给定长度为(n)的排列,(m)个询问.每次询问区间([l,r])中,最长的值域连续的一段.

    ​ 最长的值域连续的一段是指从排列的([l,r])的区间中选出一些数, 使得这些数排序后构成了连续的一段正整数, 那么这些正整数就是一个值域连续段.

    (n, m <= 5e4)

    ​ 回滚莫队 + 并查集.

    ​ 很显然莫队可做, 但是考试的时候并没有学过回滚莫队, 于是就用了普通莫队 + 线段树搞了一个(O(nsqrt n logn))的做法.

    ​ 我们发现这道题的难点就在于如何删除一个数字的影响. 那我们可以考虑只有加入操作没有删除操作. 这正好就是回滚莫队了.

    ​ 那么回滚莫队是什么呢? 其实就是莫队 + 栈.我们把加入数字时每一个影响都加入到栈中, 然后回滚的时候弹栈消除影响.

    ​ 首先, 对于左右区间在同一个快内的询问直接暴力查就好了.

    ​ 然后考虑怎么加入一个数的影响. 直接并查集合并就好了, 维护一个集合的大小(siz).

    ​ 我们找出左端点在同一个块(x)内的询问. 对于右端点, 我们已经排好序了, 是递增的, 所以只存在加入操作.对于左端点, 我们每次把左指针都回滚块(x)的右端点, 然后左移左端点, 也是只有加入操作.

    ​ 总结一下 : 右端点不断向右移, 左端点总是左移,回滚,左移,回滚......由于左端点所在块的大小为(sqrt n)的, 所以和一般莫队相比也只是乘上了2的常数.总的复杂度为(O(n sqrt n alpha)).有一个并查集的常数.

    #include <bits/stdc++.h>
    
    #define rei register int
    
    using namespace std;
    
    inline int read() {
        int s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 5e4 + 5, M = 250;
    int n, m, top, res, top1;
    int a[N], L[N], R[N], fa[N], siz[N], vis[N], pos[N], ans[N], tnp[M];
    struct ques { int l, r, id; } q[N];
    struct stack { int opt, x, y; } sta[N << 3], sta1[N << 3];
    
    int cmp(ques x, ques y) {
        return pos[x.l] == pos[y.l] ? x.r < y.r : pos[x.l] < pos[y.l];
    }
    
    int find(int x) {
        return x == fa[x] ? x : find(fa[x]);
    }
    
    void add(int x) {
        vis[a[x]] = 1;
        sta1[++ top1].opt = 1; sta1[top1].x = a[x];
        if(vis[a[x] - 1]) {
            int fx = find(a[x]), fy = find(a[x] - 1);
            if(siz[fy] < siz[fx]) swap(fx, fy);
            sta1[++ top1].opt = 2; sta1[top1].x = fx;
            fa[fx] = fy; 
            sta1[++ top1].opt = 3; sta1[top1].x = fy; sta1[top1].y = siz[fy];
            siz[fy] += siz[fx];
            res = max(res, siz[fy]);
            // cout << a[x] - 1 << " " << fy << " " << siz[fy] << "^^^
    ";
        }
        if(vis[a[x] + 1]) {
            int fx = find(a[x]), fy = find(a[x] + 1);
            if(siz[fy] < siz[fx]) swap(fx, fy);
            sta1[++ top1].opt = 2; sta1[top1].x = fx;
            fa[fx] = fy; 
            sta1[++ top1].opt = 3; sta1[top1].x = fy; sta1[top1].y = siz[fy];
            siz[fy] += siz[fx];
            res = max(res, siz[fy]);
        }
    }
    
    void add_(int x) {
        // cout << a[x] << "------>
    ";
        vis[a[x]] = 1;
        sta[++ top].opt = 1; sta[top].x = a[x];
        if(vis[a[x] - 1]) {
            // cout << "---
    ";
            int fx = find(a[x]), fy = find(a[x] - 1);
            if(siz[fy] < siz[fx]) swap(fx, fy); 
            sta[++ top].opt = 2; sta[top].x = fx; 
            fa[fx] = fy;
            sta[++ top].opt = 3; sta[top].x = fy; sta[top].y = siz[fy];
            // cout << siz[fy] << " " << siz[fx] << ")))
    ";
            siz[fy] += siz[fx];
            res = max(res, siz[fy]);
            // cout << res << "
    ";
        }
        if(vis[a[x] + 1]) { 
            // cout << "+++
    ";
            int fx = find(a[x]), fy = find(a[x] + 1);
            if(siz[fy] > siz[fx]) swap(fx, fy);
            sta[++ top].opt = 2; sta[top].x = fx;
            fa[fx] = fy;
            sta[++ top].opt = 3; sta[top].x = fy; sta[top].y = siz[fy];
            siz[fy] += siz[fx];
            res = max(res, siz[fy]);
            // cout << res << "
    ";
        }
    }
    
    void Pop_() {
        if(sta[top].opt == 0) res = sta[top].x, top --;
        if(sta[top].opt == 1) vis[sta[top].x] = 0, top --;
        if(sta[top].opt == 2) fa[sta[top].x] = sta[top].x, top --;
        if(sta[top].opt == 3) siz[sta[top].x] = sta[top].y, top --;
    }
    
    void Pop() {
        if(sta1[top1].opt == 0) res = sta1[top1].x, top1 --;
        if(sta1[top1].opt == 1) vis[sta1[top1].x] = 0, top1 --;
        if(sta1[top1].opt == 2) fa[sta1[top1].x] = sta1[top1].x, top1 --;
        if(sta1[top1].opt == 3) siz[sta1[top1].x] = sta1[top1].y, top1 --;
    }
    
    int main() {
    
        freopen("permu.in","r",stdin); freopen("permu.out","w",stdout);
    
        n = read(); m = read(); int B = sqrt(n);
        for(int i = 1;i <= n; i++) fa[i] = i, siz[i] = 1; res = 1;
        for(int i = 1;i <= n; i++) L[i] = 2333333;
        for(rei i = 1;i <= n; i++) a[i] = read(), pos[i] = (i - 1) / B + 1;
        for(int i = 1;i <= n; i++) L[pos[i]] = min(L[pos[i]], i), R[pos[i]] = max(R[pos[i]], i);
        for(rei i = 1;i <= m; i++) q[i].l = read(), q[i].r = read(), q[i].id = i;
        // for(int i = 1;i <= n; i++) cout << pos[i] << " "; cout << "
    ";
        sort(q + 1, q + m + 1, cmp);
        // for(int i = 1;i <= m; i++) cout << q[i].l << " " << q[i].r << " " << q[i].id << "
    ";
        rei x, y;
        for(rei i = 1, j;i <= m; i = j) {
            int now_k = pos[q[i].l];
            x = R[now_k] + 1; y = R[now_k];
            // cout << x << " " << y << " " << res << "
    ";
            sta1[++ top1].opt = 0; sta1[top1].x = res;
            for(j = i;pos[q[j].l] == now_k; j++) {
                if(pos[q[j].r] == pos[q[j].l]) {
                    // cout << q[j].l << " " << q[j].r << "!!!
    ";
                    int cnt = 0;
                    for(int k = q[j].l;k <= q[j].r; k++) tnp[++ cnt] = a[k];
                    sort(tnp + 1, tnp + cnt + 1);
                    int cop = 1, cip = 1;
                    for(int k = 2;k <= cnt; k++)  
                        if(tnp[k] == tnp[k - 1] + 1) cop ++, cip = max(cip, cop);
                        else cop = 1;
                    ans[q[j].id] = cip;
                }
                else {
                    // cout << q[j].l << " " << q[j].r << ")))
    ";
                    while(y < q[j].r) add(++ y);
                    // cout << j << ":" << res << "!!!
    ";
                    sta[++ top].opt = 0; sta[top].x = res;
                    while(x > q[j].l) add_(-- x);
                    ans[q[j].id] = res;
                    while(top) Pop_();
                    x = R[now_k] + 1;
                }
            }
            while(top1) Pop();
        }
        for(rei i = 1;i <= m; i++) printf("%d
    ", ans[i]);
    
        fclose(stdin); fclose(stdout);
    
        return 0;
    }
    
    /*
    8 3
    3 1 7 2 5 8 6 4
    1 4
    5 8
    1 7
    */
    

    (上面代码写的太麻烦了, 其实好多东西都可以合并到一起去的).

    T2

    ​ 有一颗(n)个点的树, 有三个点在树上轮流取点,直到点被取完.

    ​ 取完之后需要计算每一个人的得分.有(m)个幸运数, 对于一个点对((u, v)), 如果它们在原图上的距离为一个幸运数, 并且被同一个人取到, 那么这个人就得到一分.

    ​ 假设每个人取点时都是等概率的选取一个未被选过的点, 问每个人得分的期望.

    (n <= 5e4, m <= 10).

    ​ 点分治.

    ​ (考场上完全没有思路啊)

    ​ 对于每个人其实都是可以分开算的, 因为选取每个点的概率都是相等的.

    ​ 假设一个人需要选择(k)个点, 那么总共就有(C_n^k)中方案, 对于某一个点对的选取概率是 : (frac{C_{n-2}^{k-2}}{C_n^k} = frac{k*(k-1)}{n*(n-1)}).意思就是当前的点对已经选好了, 剩下(k-2)个点随便选.

    ​ 每一个点对的概率都是这样的, 所以现在问题转化成了, 计算有多少个点对的距离为幸运数, 那么直接点分治就好了, 复杂度(O(mnlogn)).

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }   
    
    const int N = 5e4 + 5;
    int n, m, rt, cnt, totsiz;
    int k[11], dis[N], tmp[N], siz[N], num[N], vis[N], head[N], tong[N], Tmp[N], Tong[N], maxsiz[N];
    struct edge { int to, nxt; } e[N << 1];
    
    void add(int x, int y) {
        e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y;
    }
    
    void get_rt(int x, int fa) {
        siz[x] = 1; maxsiz[x] = 0;
        for(int i = head[x]; i ; i = e[i].nxt) {
            int y = e[i].to; if(vis[y] || y == fa) continue ;
            get_rt(y, x); siz[x] += siz[y];
            maxsiz[x] = max(maxsiz[x], siz[y]);
        }
        maxsiz[x] = max(maxsiz[x], totsiz - siz[x]);
        if(maxsiz[rt] > maxsiz[x]) rt = x;
    }
    
    void get_dis(int x, int fa) {
        if(!tong[dis[x]] && dis[x]) tmp[++ cnt] = dis[x];
        tong[dis[x]] ++;
        for(int i = head[x]; i ; i = e[i].nxt) {
            int y = e[i].to; if(y == fa || vis[y]) continue ;
            dis[y] = dis[x] + 1;
            get_dis(y, x);
        }
    }
    
    void calc(int x) {
        // cout << x << "--------->
    ";
        int Cnt = 0; dis[x] = 0;
        for(int ii = head[x]; ii ; ii = e[ii].nxt) {
            int y = e[ii].to; if(vis[y]) continue ;
            dis[y] = dis[x] + 1;
            for(int i = 1;i <= cnt; i++) tong[tmp[i]] = 0; cnt = 0;
            get_dis(y, x); 
            sort(tmp + 1, tmp + cnt + 1);
            // cout << y << "---->
    ";
            // for(int i = 1;i <= cnt; i++) cout << tmp[i] << " "; cout << "
    "; 
            // for(int i = 1;i <= cnt; i++) cout << tong[tmp[i]] << " "; cout << "
    ";
            for(int i = 1;i <= m; i++) {
                num[i] += tong[k[i]];
                for(int j = 1;j <= cnt; j++) { 
                    if(tmp[j] >= k[i]) break ;
                    num[i] += tong[tmp[j]] * Tong[k[i] - tmp[j]];
                }
                // cout << i << ":" << num[i] << "
    ";
            }
            for(int i = 1;i <= cnt; i++) {
                if(!Tong[tmp[i]]) Tmp[++ Cnt] = tmp[i];
                Tong[tmp[i]] += tong[tmp[i]];
            }
        }
        for(int i = 1;i <= Cnt; i++) Tong[tmp[i]] = tong[tmp[i]] = 0;
    }
    
    void solve(int x) {
        calc(x); vis[x] = 1;
        for(int i = head[x]; i ; i = e[i].nxt) {
            int y = e[i].to; if(vis[y]) continue ;
            maxsiz[0] = totsiz = siz[y]; rt = 0;
            get_rt(y, 0); solve(rt);
        }
    }
    
    int t[4];
    void calc_sum(int x) {
        double k_ = 1.0 * t[x] * (t[x] - 1) / (1.0 * n * (n - 1)), sum = 0;
        for(int i = 1;i <= m; i++) sum += k_ * num[i];
        printf("%.2lf
    ", sum);
    }
    
    int main() {
    
        freopen("game.in","r",stdin); freopen("game.out","w",stdout);
    
        n = read(); m = read();
        for(int i = 1;i <= m; i++) k[i] = read();
        for(int i = 1, x, y;i < n; i++) {
            x = read(); y = read(); add(x, y); add(y, x);
        }
        maxsiz[0] = totsiz = n; rt = 0;
        get_rt(1, 0); solve(rt);
        // for(int i = 1;i <= m; i++) cout << i << ":" << k[i] << " " << num[i] << "
    ";
        int t1 = n / 3, t2 = n % 3;
        if(t2 == 0) t[1] = t[2] = t[3] = t1;
        if(t2 == 1) t[1] = t1 + 1, t[2] = t[3] = t1;
        if(t2 == 2) t[1] = t[2] = t1 + 1, t[3] = t1;
        for(int i = 1;i <= 3; i++) calc_sum(i);
    
        fclose(stdin); fclose(stdout);
    
        return 0;
    }
    
    /*
    5 3
    1 2 3
    1 2
    1 5
    2 3
    2 4
    */
    

    T3

    ​ 给定一张(n)个点的无向图, 要求把(n)个点都分入(A, B)集合中.

    ​ 若点(i, j)不相连, 则(i, j)不可以同时放入(A)集合中;

    ​ 若点(i, j)相连,则(i, j)不可以同时放入(B)集合中;

    (A, B)集合都不可以为空.

    ​ (相连是指有直接连边)

    ​ 问有多少种方案可以满足上面的条件.

    (n <= 5000).

    ​ 2-sat.

    ​ 一个点只能有两种选择, 显然2-sat.

    ​ 如果两个点(x,y)相连, 那么连((x+n ightarrow y), (y+n ightarrow x)).

    ​ 如果两个点(x, y)不相连, 那么连((x ightarrow y + n)(y ightarrow x +n)).

    ​ 然后跑Tarjan判断有无解.

    ​ 主要是怎么找到所有方案.

    ​ 首先找到一个初始方案, 我们发现要得到其他方案只有这三种方式:

    ​ 1.找一个(A)集合中的点(x)放到(B)集合, 条件是与(x)相连的所有点都不可以在(B)集合.

    ​ 2.找一个(B)集合中的点(y)放到(A)集合, 条件是与(y)不相连的所有点都不可以在(A)集合.

    ​ 3.找一个(A)集合中的点(x)(B)集合中的点(y), 把它们交换, 条件是除了(y)所有与(x)相连的点都不可以在(B)集合, 除了(x)所有与(y)不相连的点都不可以在(A)集合.

    ​ 为什么只有这三种情况呢? 假设我们要从(A)集合移动两个点到(B)集合, 那么这两个点一定是有直接连边的, 那么它们就不能同时存在于(B)集合.所以不管移动那个集合, 一定是只能移动一个点的.

    ​ 记得最后判断一下(A, B)不为空就好了.

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 5005, M = 3e7;
    int n, cnt, tot, top, css, siz1, siz2;
    int in[N << 1], col[N << 1], dfn[N << 1], sta[N << 1], low[N << 1], head[N << 1], inse[N], link[N][N];
    struct edge { int to, nxt; } e[M];
    vector <int> v[N];
    
    void add(int x, int y) {
        e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y;
    }
    
    void Tarjan(int x) {
        dfn[x] = low[x] = ++ tot; in[x] = 1; sta[++ top] = x;
        for(int i = head[x]; i ; i = e[i].nxt) {
            int y = e[i].to; 
            if(!dfn[y]) Tarjan(y), low[x] = min(low[x], low[y]);
            else if(in[y]) low[x] = min(low[x], dfn[y]);
        }
        if(dfn[x] == low[x]) {
            int p; css ++;
            do {
                in[p = sta[top --]] = 0;
                col[p] = css;
            } while(p != x);
        }
    }
    
    void Sta_pro() {
        int ans;
        if(siz1 == 0 || siz2 == 0) ans = 0; // ***
        else ans = 1;
        for(int x = 1;x <= n; x++) {
            if(!inse[x] && siz1 > 1) { // ***
                int f = 0;
                for(int i = 0;i < (int) v[x].size(); i++)
                    if(inse[v[x][i]]) { f = 1; break; }
                if(!f) ans ++;
            }
            if(inse[x] && siz2 > 1) { // ***
                int f = 0;
                for(int y = 1;y <= n; y++) {
                    if(y == x || link[x][y]) continue ;
                    if(!inse[y]) { f = 1; break ; } 
                }
                if(!f) ans ++;
            }
        }
        for(int x = 1;x <= n; x++) 
            for(int y = 1;y <= n; y++) 
                if(!inse[x] && inse[y]) {
                    int f = 0;
                    for(int i = 0;i < (int) v[x].size(); i++) 
                        if(inse[v[x][i]] && v[x][i] != y) { f = 1; break ; }
                    if(f) continue ;
                    for(int z = 1;z <= n; z++) {
                        if(z == x || z == y || link[z][y]) continue ;
                        if(!inse[z]) { f = 1; break ; }
                    }
                    if(!f) ans ++;
                }
        printf("%d", ans);
    }
    
    void Work2() {
        for(int i = 1;i <= n; i++)
            for(int j = i + 1;j <= n; j++) 
                if(link[i][j]) add(i + n, j), add(j + n, i);
                else add(i, j + n), add(j, i + n);
        for(int i = 1;i <= 2 * n; i++) if(!dfn[i]) Tarjan(i);
        // for(int i = 1;i <= n; i++) cout << i << ":" << col[i] << " " << col[i + n] << "
    ";
        for(int i = 1;i <= n; i++) if(col[i] == col[i + n]) {
            printf("0"); return ;
        }
        for(int i = 1;i <= n; i++) inse[i] = col[i] < col[i + n] ? 0 : 1;
        for(int i = 1;i <= n; i++) 
            if(!inse[i]) siz1 ++;
            else siz2 ++;
        // for(int i = 1;i <= n; i++) cout << i << ":" << inse[i] << "
    ";
        Sta_pro();
    }
    
    int main() {
    
        freopen("conspiracy.in","r",stdin); freopen("conspiracy.out","w",stdout);
    
        n = read();
        for(int i = 1, t;i <= n; i++) {
            t = read();
            for(int j = 1, x;j <= t; j++) 
                x = read(), v[i].push_back(x), link[i][x] = 1;
        }
        Work2();
    
        fclose(stdin); fclose(stdout);
    
        return 0;
    }
    
    /*
    4
    2 2 3
    2 1 3
    3 1 2 4
    1 3
    */
    
  • 相关阅读:
    好文章记录
    求职经历
    C正确初始化方式
    linux 常用命令
    MYSQL查找从小到大排列第90%个位置的数据
    最好的单例模式
    <%= %>和${}使用差异
    后台和jsp乱码处理
    浏览器下载文件
    文件下载
  • 原文地址:https://www.cnblogs.com/czhui666/p/14461635.html
Copyright © 2020-2023  润新知