zoukankan      html  css  js  c++  java
  • 18.9.22 考试总结

      

    这道题一看就是可持久化并查集 然后我就愉快的yy了一波 还是错掉了qwqwqwqwq

    方法是对的 就是我每次在树上查询$fa$的时候我还压缩了路径 导致这玩意空间炸掉了

    所以要保证时间复杂度 就启发式合并 也就是$size$小的往$size$大的搞

    这样子就保证每次合并的时候连通块元素个数每次至少乘以$2$ 也就是保证了层数是$log$级的

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e5 + 5;
    int n,f[90 * N],ls[90 * N],rs[90 * N],size[90 * N],root[N];
    int cnt,m,fa[N];
    
    int build(int o, int l ,int r) {
        
        int nd = ++ cnt;
        if(l == r) {
            f[nd] = l; size[nd] = 1;
            return nd;
        }
        int mid = (l + r) >> 1;
        ls[nd] = build(2 * o, l, mid);
        rs[nd] = build(2 * o + 1, mid + 1, r);
        return nd;
    }
    
    void Init( ) {
        
        scanf("%d%d",& n,& m);
        root[0] = build(1, 1, n);
    }
    
    int modify_fa(int pre, int o, int l, int r, int pos, int del) {
        
        int nd = ++ cnt;
        f[nd] = f[pre],ls[nd] = ls[pre],rs[nd] = rs[pre],size[nd] = size[pre];
        if(l == r) {
            f[nd] = del; return nd;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) {
            ls[nd] = modify_fa(ls[pre], 2 * o, l, mid, pos, del);
        }
        else rs[nd] = modify_fa(rs[pre], 2 * o + 1, mid + 1, r, pos, del);
        return nd;
    }
    
    int modify_size(int pre, int o, int l, int r, int pos, int del) {
        
        int nd = ++ cnt;
        f[nd] = f[pre],ls[nd] = ls[pre],rs[nd] = rs[pre],size[nd] = size[pre] + del;
        if(l == r) {
            return nd;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) {
            ls[nd] = modify_size(ls[pre], 2 * o, l, mid, pos, del);
        }
        else rs[nd] = modify_size(rs[pre], 2 * o + 1, mid + 1, r, pos, del);
        return nd;
    }
    
    int query_fa(int nd, int o, int l, int r, int pos) {
        
        if(l == r) {
            return f[nd];
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) return query_fa(ls[nd], 2 * o, l, mid, pos);
        else return query_fa(rs[nd], 2 * o + 1, mid + 1, r, pos);
    }
    
    int query_size(int nd, int o, int l, int r, int pos) {
        
        if(l == r) {
            return size[nd];
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) return query_size(ls[nd], 2 * o, l, mid, pos);
        else return query_size(rs[nd], 2 * o + 1, mid + 1, r, pos);
    }
    
    int Find_fa(int x, int M) {
        
        int ff = query_fa(root[x], 1, 1, n, M);
        if(ff == M) return ff;
        int F = Find_fa(x, ff);
        return F;
    }
    
    void Solve( ) {
        
        int las = 0;
        for(int i = 1;i <= m;i ++)
         {
            int opt,x,y;
            scanf("%d",& opt);
            if(opt == 1) {
                scanf("%d%d",& x,& y); x = x + las, y = y + las;
                int fa1 = Find_fa(i - 1, x), fa2 = Find_fa(i - 1, y);
                if(fa1 == fa2) {
                    root[i] = root[i - 1]; continue;
                }
                int s1 = query_size(root[i - 1], 1, 1, n, fa1);
                int s2 = query_size(root[i - 1], 1, 1, n, fa2);
                if(s1 < s2) { swap(s1,s2); swap(fa1, fa2); }
                root[i] = modify_fa(root[i - 1], 1, 1, n, fa2, fa1);
                root[i] = modify_size(root[i], 1, 1, n, fa1, s2);
            }
            else {
                scanf("%d%d",& x,& y); x = x + las, y = y + las;
                root[i] = root[i - 1]; 
                int fat = Find_fa(x, y);
                int s = query_size(root[x], 1, 1, n, fat);
                printf("%d
    ",s); las = s;
            }  
        }
    }
    
    int main( ) {
        
        freopen("build.in","r",stdin);
        freopen("build.out","w",stdout);
        Init( );
        Solve( );
    }

     

    这道题班上有人用贪心A掉了

    而蒟蒻我只会垃圾dp  qwqwq 首先我们可以发现这个玩意正着来搞是不行的

    因为每个人一旦选择这座城市自己要 那么他的下一个人的选择是确定的

    并且后面的选择跟前面的毫无联系 就无法体现最优选择的思想 所以我们考虑倒着搞 维护一个$sum$前缀和

    $dp[i][0/1][0/1]$表示到了第$i$个城市 当前是谁的回合 这个人选不选择这个城市 这个人获得的最大收益

            $dp[i][now][1] = sum[n] - sum[i] + a[i] - max(dp[i +1][now xor 1][0],dp[i + 1][now xor 1][1])$ 

    表示如果我选择这座城市 相当于把主动权给别人 那么下个人一定会选择它的最优方案 总的收益是一定的 所以就用总收益减去别人的收益就可以了

            $dp[i][now][0] = max(dp[i + 1][now][1],dp[i + 1][now][0])$

    表示如果我这个位置不选 我的最大收益就是后面的选择所带来的最大收益

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 1e5 + 5;
    int n;
    ll dp[N][2][2],sum[N],a[N];
    
    void Init( ) {
        
        scanf("%d",& n);
        for(int i = 1;i <= n;i ++) {
            scanf("%lld",& a[i]);
            sum[i] = sum[i - 1] + a[i];
        }
    }
    
    void Solve( ) {
        
        dp[n][1][1] = a[n], dp[n][0][1] = a[n];
        for(int i = n - 1;i >= 1;i --) {
            for(int now = 0;now <= 1;now ++) {
                dp[i][now][1] = a[i] + sum[n] - sum[i] - max(dp[i + 1][now ^ 1][0], dp[i + 1][now ^ 1][1]);
                dp[i][now][0] = max(dp[i + 1][now][1], dp[i + 1][now][0]);
            }
        }
        printf("%lld
    ",max(dp[1][1][1], dp[1][1][0]));
    }
    
    int main( ) {
        
        freopen("distribute.in","r",stdin);
        freopen("distribute.out","w",stdout);
        Init( );
        Solve( );
    }

    然后我考试的时候做不来这道题 

    emmmmmm考虑要存在合法的圆边那么他肯定存在于一个环里面 在无向图里面 他肯定是一个点双连通分量

    所以把每个点双处理出来 如果边数等于点数 他就是一个合法简单环 否则会有不止一个环 就不合法

    考虑为什么是点双连通 不是边双呢 因为只有点双才是多个简单环叠加 

    左边这个虽然是边双 但是他是合法的 若是按照之前的方法 他会被判断成不合法 因为他不是简单环的叠加 而点双就一定是叠加 判断就一定合法

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e5 + 5;
    int n,head[N],nex[2 * N],tov[2 * N],id[2 * N],c[N];
    int dfn[N],low[N],idc,tot = 1,m,stk[2 * N],top,col,que[2 * N];
    bool vis[2 * N],isans[2 * N];
    
    void add(int u, int v, int ID) {
        
        tot ++;
        nex[tot] = head[u];
        tov[tot] = v; id[tot] = ID;
        head[u] = tot;
    }
    
    void Init( ) {
        
        scanf("%d%d",& n,& m);
        for(int i = 1;i <= m;i ++) {
            int u,v;
            scanf("%d%d",& u,& v);
            add(u, v, i); add(v, u, i);
        }
    }
    
    void tarjan(int u,int from) {
        
        low[u] = dfn[u] = ++idc;
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if((i ^ 1) == from) continue;
            if(! vis[i]) vis[i] = vis[i ^ 1] = true, stk[++ top] = i;
            if(! dfn[v]) {
                tarjan(v, i);
                low[u] = min(low[u], low[v]);
                if(low[v] >= dfn[u]) {
                    col ++; 
                    int cntn = 0,cnte = 0;
                    while(1) {
                        int e = stk[top --];
                        if(c[tov[e]] != col) c[tov[e]] = col,cntn ++;
                        if(c[tov[e ^ 1]] != col) c[tov[e ^ 1]] = col, cntn ++;
                        que[++ cnte] = e;
                        if(e == i) break;
                    }
                    if(cnte == cntn) {
                        for(int i = 1;i <= cnte;i ++) 
                            isans[que[i]] = isans[que[i] ^ 1] = true;
                    }
                }
            }
            else if(low[u] > dfn[v]) low[u] = min(low[u], dfn[v]);
        }
    }
    
    void Solve( ) {
        
        for(int i = 1;i <= n;i ++) {
            if(! dfn[i]) tarjan(i, 0);
        }
        int ans = 0;
        for(int i = 2;i <= tot;i += 2) if(isans[i]) ans ++;
        printf("%d
    ",ans);
        for(int i = 2;i <= tot;i += 2) 
            if(isans[i]) printf("%d ",i / 2);
    }
    
    int main( ) {
        
        freopen("find.in","r",stdin);
        freopen("find.out","w",stdout);
        Init( );
        Solve( );
    }
  • 相关阅读:
    助教观察记录5(10/21-11/3)
    助教观察记录4(10/07-10/20)
    助教观察记录3(9/23-10/06)
    助教观察记录1(9/5-9/15)
    2019年春季学期《C语言程序设计II》课程总结
    2020软件工程个人作业06——软件工程实践总结作业
    软件工程第二次作业
    2020软件工程作业3
    2020软件工程作业01
    神必高考数学题乱写
  • 原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9690596.html
Copyright © 2011-2022 走看看