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\n",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\n",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\n",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( );
}

猜你喜欢

转载自www.cnblogs.com/Rubenisveryhandsome/p/9690596.html