【题录】Atcoder ACL#1
C.Moving Pieces
走的步数与路径无关,只与棋子的初末位置有关。并且棋子的路径不会碰撞-如果碰撞,可以理解为移动了另外的一颗棋子。因此使用费用流跑一下求出最大收益。
#include <bits/stdc++.h> using namespace std; #define maxn 3000000 #define INF 99999999 #define N 505 int n, m, num, Si, Sj, S, T, dis[maxn], fl[maxn]; int a[N][N], tag[maxn], mark[N][N], pre[maxn]; char s[N]; deque <int> q; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } struct edge { int cnp = 2, to[maxn], last[maxn], head[maxn], f[maxn], w[maxn]; void add(int u, int v, int fl, int co) { last[cnp] = head[u], to[cnp] = v, f[cnp] = fl, w[cnp] = co, head[u] = cnp ++; last[cnp] = head[v], to[cnp] = u, f[cnp] = 0, w[cnp] = -co, head[v] = cnp ++; } }E; int id(int x, int y) { return (x - 1) * m + y; } void dfs(int x, int y) { if(x > n || y > m) return; if(a[x][y] == 1) return; if(mark[x][y] == num) return; mark[x][y] = num; E.add(num, id(x, y) + n * m, 1, x + y - Si - Sj); dfs(x + 1, y); dfs(x, y + 1); } bool SPFA() { for(int i = 1; i <= n * m * 2 + 2; i ++) dis[i] = -INF, fl[i] = 0; dis[S] = 0, fl[S] = INF; tag[S] = 1; q.push_back(S); while(!q.empty()) { int u = q.front(); q.pop_front(); tag[u] = 0; for(int i = E.head[u]; i; i = E.last[i]) { int v = E.to[i]; if(dis[v] < dis[u] + E.w[i] && E.f[i]) { dis[v] = dis[u] + E.w[i]; pre[v] = i; fl[v] = min(fl[u], E.f[i]); if(!tag[v]) { if((!q.empty()) && (dis[v] > dis[q.front()])) q.push_front(v); else q.push_back(v); tag[v] = 1; } } } } return fl[T]; } int Solve() { int ans = 0; while(SPFA()) { int now = T; ans += fl[T] * dis[T]; do { int t = pre[now]; E.f[t] -= fl[T], E.f[t ^ 1] += fl[T]; now = E.to[t ^ 1]; }while(now != S); } return ans; } int main() { n = read(), m = read(); for(int i = 1; i <= n; i ++) { scanf("%s", s + 1); for(int j = 1; j <= m; j ++) if(s[j] == '#') a[i][j] = 1; else if(s[j] == 'o') a[i][j] = 2; } for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) { if(a[i][j] != 2) continue; num = (i - 1) * m + j; Si = i, Sj = j; dfs(i, j); } S = n * m * 2 + 1, T = n * m * 2 + 2; for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) if(a[i][j] == 2) E.add(S, id(i, j), 1, 0); for(int i = 1; i <= n * m; i ++) E.add(i + n * m, T, 1, 0); printf("%d\n", Solve()); return 0; }
D.Keep Distances
按照贪心的策略可以从第一个开始,能够跳跃一步就跳跃一步直到跳出去为止。这样可以求出最大集合的大小。考虑把这些跳跃到的点标记为 \(a_{1}, a_{2}, ..., a_{k}\)。再反向贪心一次,同样把跳跃到的点从小到大标记为 \(b_{1}, b_{2}, ..., b_{k}\)。则 \([a_{i}, b_{i}]\) 之间的点均出现在最大集合中,而\((b_{i}, a_{i + 1})\) 这样其余部分的点均不会出现在最大集合中。
#include <bits/stdc++.h> using namespace std; #define maxn 300000 #define MXB 19 int n, K, X[maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } struct edge { int cnp = 1, opt, last[maxn], head[maxn], to[maxn], deg[maxn], gra[maxn][MXB], sum[maxn][MXB]; void add(int u, int v) { to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++; deg[v] ++; } void dfs(int u, int fa) { gra[u][0] = fa; for(int i = 1; i < MXB; i ++) gra[u][i] = gra[gra[u][i - 1]][i - 1]; sum[u][0] = u + opt; for(int i = 1; i < MXB; i ++) sum[u][i] = sum[u][i - 1] + sum[gra[u][i - 1]][i - 1]; for(int i = head[u]; i; i = last[i]) { int v = to[i]; dfs(v, u); } } bool cmp(int a, int b) { if(!opt) return a <= b; else return a >= b; } int Jump(int a, int b) { for(int i = MXB - 1; i >= 0; i --) { if(gra[a][i] && cmp(gra[a][i], b)) a = gra[a][i]; } return a; } int Cal(int a, int b) { if(opt && a < b) return 0; if(!opt && a > b) return 0; int ans = 0; for(int i = MXB - 1; i >= 0; i --) if(gra[a][i] && cmp(gra[a][i], b)) ans += sum[a][i], a = gra[a][i]; ans += sum[a][0]; return ans; } }E, E2; int main() { n = read(), K = read(); E.opt = 0, E2.opt = 1; E.opt = 0, E2.opt = 1; for(int i = 1; i <= n; i ++) X[i] = read(); for(int i = 1; i <= n; i ++) { int l = 1, r = i; while(l < r) { int mid = (l + r) >> 1; if(mid + 1 <= r) mid += 1; if(X[i] - X[mid] >= K) l = mid; else r = mid - 1; } if(X[i] - X[l] >= K) E2.add(l, i); } // return 0; for(int i = 1; i <= n; i ++) { int l = i + 1, r = n; while(l < r) { int mid = (l + r) >> 1; if(X[mid] - X[i] >= K) r = mid; else l = mid + 1; } if(X[l] - X[i] >= K) E.add(l, i); } for(int i = 1; i <= n; i ++) if(!E.deg[i]) E.dfs(i, 0); for(int i = 1; i <= n; i ++) if(!E2.deg[i]) E2.dfs(i, 0); int Q = read(); for(int i = 1; i <= Q; i ++) { int l = read(), r = read(); int t = E.Jump(l, r); int t2 = E2.Jump(r, l); printf("%d\n", E2.Cal(r, t2) - E.Cal(l, t)); } return 0; }
E.Shuffle Window
可以把操作理解为手上时刻有K张牌,每次随机打出一张后可以从牌堆中再摸一张(按顺序)。这样的话考虑独立的两张牌,有两种情况:第一张牌打出去的时候第二张牌还没有拿到手上;第一张牌还未打出时已经摸到了第二张牌。第一种情况下逆序对的个数不会改变,而第二种情况下则有二分之一的可能性可以改变原有的逆序对/创造一个新的逆序对。
#include <bits/stdc++.h> using namespace std; #define mod 998244353 #define maxn 2000000 #define lowbit(i) (i & (-i)) int n, K, ans, a[maxn], f[maxn], inv2, sum, fac[maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } int mul(int a, int b) { return 1ll * a * b % mod; } int add(int a, int b) { a += b; if(a >= mod) a -= mod; return a; } int sub(int a, int b) { a -= b; if(a < 0) a += mod; return a; } void Up(int &a, int b) { a += b; if(a >= mod) a -= mod; } int Qpow(int x, int t) { int base = 1; for(; t; t >>= 1, x = mul(x, x)) if(t & 1) base = mul(base, x); return base; } struct FW { int C[maxn]; void Mod(int x, int y) { for(int i = x; i <= n; i += lowbit(i)) Up(C[i], y); } int Cal(int x) { int ans = 0; for(int i = x; i; i -= lowbit(i)) Up(ans, C[i]); return ans; } }C1; signed main() { n = read(), K = read(); inv2 = Qpow(2, mod - 2); int invK = Qpow(K, mod - 2), P = mul(K - 1, invK); for(int i = 1; i <= n; i ++) a[i] = read(); for(int i = K + 1; i <= n; i ++) f[i] = f[i - 1] + 1; for(int i = 1; i <= n; i ++) { int num = C1.Cal(a[i] - 1); Up(ans, mul(inv2, mul(Qpow(P, f[i]), num))); num = sub(sum, num); ans = sub(ans, mul(inv2, mul(Qpow(P, f[i]), num))); Up(sum, Qpow(Qpow(P, f[i]), mod - 2)); C1.Mod(a[i], Qpow(Qpow(P, f[i]), mod - 2)); } for(int i = 1; i <= n; i ++) C1.C[i] = 0; for(int i = 1; i <= n; i ++) { int t = C1.Cal(a[i] - 1); Up(ans, i - 1 - t); C1.Mod(a[i], 1); } printf("%d\n", ans); return 0; }