这道题一看就是可持久化并查集 然后我就愉快的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( ); }