【题录】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;
}

 

posted @ 2020-10-10 17:54  Twilight_Sx  阅读(130)  评论(0编辑  收藏  举报