ECFinal2020 題解


ECFinal2020 題解

A.Random Shuffle

每一個時刻每一位可以表示成其余位的異或。

可以用bitset記錄每一位和其它位之間的異或方程,記為\(f_i\)

比如對於左移13步,可以令\(f_i\leftarrow f_i\text{xor}f_{i-13}\)

如果知道\(\mod x\)的值,我們也就知道了當前這個時刻的后\(k\)位,\(k\)是最大滿足\(2^k|x\)的值。

\(n\geq 50\)的條件下,至少有\(47\)個方程,得到\(17\)個自由元。

枚舉這17個判斷即可。

時間復雜度為\(O(2^{17}+N)\)

code:

const int MAXN = 1e5 + 233;
typedef bitset<65> bs;
bs f[200];
int n, a[MAXN], pos[MAXN], gen[MAXN];
vector<bs> func;
unsigned LL seed;
bs zzz;
vector<int> fr;
bool val[64];
void gauss() {
    while (func.size() > 64) func.POB();
    while (func.size() < 64) func.PB(zzz);
    rep(i, 64) {
        bool ok = 0;
        for (int j = i; j < 64; ++j) {
            if (func[j][i]) {
                swap(func[j], func[i]);
                ok = 1;
                break;
            }
        }
        if (!ok) {
            fr.PB(i);
            continue;
        }
        rep(j, 64) {
            if (j != i)
                if (func[j][i])
                    func[j] ^= func[i];
        }
    }
}
bool check(unsigned LL Tmp) {
    seed = Tmp;
    rb(i, 1, n) {
        seed ^= seed << 13;
        seed ^= seed >> 7;
        seed ^= seed << 17;
        if (seed % i != gen[i])
            return false;
    }
    return true;
}
int main() {
    rep(i, 64) f[i].set(i);
    scanf("%d", &n);
    rb(i, 1, n) scanf("%d", &a[i]), pos[a[i]] = i;
    rl(i, n, 1) {
        gen[i] = pos[i] - 1;
        int oth = pos[i];
        swap(pos[i], pos[a[i]]);
        swap(a[oth], a[i]);
    }
    rb(T, 1, min(70, n)) {
        rl(i, 63, 13) f[i] ^= f[i - 13];
        rb(i, 0, 63) f[i] ^= f[i + 7];
        rl(i, 63, 17) f[i] ^= f[i - 17];
        rep(i, 64) if ((T >> i) & 1) { break; }
        else {
            bs tmp = f[i];
            if ((gen[T] >> i) & 1)
                tmp.set(64);
            func.PB(tmp);
        }
    }
    gauss();
    rep(mask, 1 << fr.size()) {
        unsigned LL tmp = 0;
        rep(i, fr.size()) {
            if ((mask >> i) & 1) {
                tmp |= (unsigned LL)(1) << fr[i];
            }
            val[fr[i]] = (mask >> i) & 1;
        }
        rep(i, 64) {
            if (func[i][i]) {
                bool va = func[i][64];
                rep(j, 64) if (func[i][j]) va ^= val[j];
                if (va) {
                    tmp |= (unsigned LL)(1) << i;
                }
            }
        }
        if (check(tmp)) {
            cout << tmp << endl;
            return 0;
        }
    }
    return 0;
}

B.City Brain

假設我們已知路徑求分配方案。

考慮將\(k\)元,分配到\(y\)個路徑:

由於\(\frac{1}{x}\)越往后減少越緩慢,所以分配的一定是越平均越好,這個是可以\(O(1)\)計算的。

其實只需要知道有多少路徑經過2人和1人,設為\(x\)\(y\)

對於每一個\(x\),其實只需要找打對應最小的\(y\),這個部分可以直接枚舉相遇點和相離點,\(O(N^2)\)預處理。

對於每一個\(x,y\),考慮各分配多少,由於這是兩個凸函數相加,所以是單峰的,直接三分即可。

code:

const int MAXN = 5005;
int n, m, k, s1, t1, s2, t2;
vector<int> g[MAXN];
int dis[MAXN][MAXN];
int dp[MAXN];
double calc(int tot, int cnt) {
    double have = tot / cnt;
    double rst = tot % cnt;
    double oth = cnt - rst;
    return oth * (1 - 1 / (1 + have)) + rst * (1 - 1 / (1 + (have + 1.0)));
}
int main() {
    scanf("%d%d%d", &n, &m, &k);
    rb(i, 1, m) {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].PB(v);
        g[v].PB(u);
    }
    memset(dis, 63, sizeof(dis));
    rb(i, 1, n) {
        queue<int> q;
        q.push(i);
        dis[i][i] = 0;
        while (!q.empty()) {
            int now = q.front();
            q.pop();
            for (auto it : g[now])
                if (dis[i][it] == INF) {
                    dis[i][it] = dis[i][now] + 1;
                    q.push(it);
                }
        }
    }
    scanf("%d%d%d%d", &s1, &t1, &s2, &t2);
    memset(dp, 63, sizeof(dp));
    rb(i, 1, n) rb(j, 1, n) if (dis[i][j] != INF && dis[s1][i] != INF && dis[s2][i] != INF &&
                                dis[j][t1] != INF && dis[j][t2] != INF)
        check_min(dp[dis[i][j]], dis[s1][i] + dis[s2][i] + dis[j][t1] + dis[j][t2]);
    dp[0] = dis[s1][t1] + dis[s2][t2];
    double rest = 1e18;
    rb(i, 0, n) {
        if (dp[i] != INF) {
            double tmp = 2.0 * i + dp[i];
            if (!i && dp[i] == 0) {
                rest = 0;
                break;
            }
            if (i == 0)
                tmp -= calc(k, dp[i]);
            else if (dp[i] == 0)
                tmp -= 2.0 * calc(k, i);
            else {
                int l = 0, r = k;
                while (l < r) {
                    int mid = (l + r) >> 1;
                    if (2.0 * calc(mid, i) + calc(k - mid, dp[i]) <
                        2.0 * calc(mid + 1, i) + calc(k - (mid + 1), dp[i]))
                        l = mid + 1;
                    else
                        r = mid;
                }
                tmp -= 2.0 * calc(l, i) + calc(k - l, dp[i]);
            }
            check_min(rest, tmp);
        }
    }
    printf("%.15Lf\n", rest);
    return 0;
}

C.Prof. Pang's sequence

離線處理詢問。

掃描右端點,遇到一個值\(x\)時翻轉當前位置和\(x\)上一次出現的位置之間的奇偶狀態,若是奇數則帶來1的貢獻。

對於詢問,只需要查詢\([l,r]\)中的歷史版本和。

可以用線段樹實現,具體實現方法:

對於每一個節點記錄:

  1. 上一次更新這個節點的時間。
  2. 從上一次pushdown開始有多長時間處於翻轉狀態,有多長時間處於不翻轉狀態
  3. 當前區間1和0的個數。
  4. 歷史和:ans

pushdown:

  1. 計算從上一次更新經過的時間,加到對應狀態上。

  2. 根據各狀態的時間算對歷史答案的貢獻。

  3. 將各狀態的時間傳遞到兒子,需要更具兒子處是否翻轉考慮是否取反。

  4. 傳遞tag。

  5. 將最新的更新時間設為當前時間。

pushup:

​ ans和1,0的個數直接繼承兒子的值。

code:

const int N = 1 << 19;
int nowt;
struct node {
    int tag, sum[2], cnt[2], t;
    LL ans;
    node() { memset(sum, 0, sizeof(sum)), memset(cnt, 0, sizeof(cnt)), tag = ans = t = 0; }
};
node tree[N + N];
LL ans[N];
vector<mp> que[N];
map<int, int> pre;
void pd(int now) {
    tree[now].sum[tree[now].tag] += nowt - tree[now].t;
    tree[now].t = nowt;
    tree[now].ans += 1ll * tree[now].cnt[1] * tree[now].sum[0] + 1ll * tree[now].cnt[0] * tree[now].sum[1];
    if (now < N) {
        bool lt, rt;
        lt = tree[now << 1].tag;
        rt = tree[now << 1 | 1].tag;
        rep(i, 2) {
            tree[now << 1].sum[lt ^ i] += tree[now].sum[i];
            tree[now << 1 | 1].sum[rt ^ i] += tree[now].sum[i];
        }
        tree[now << 1].tag ^= tree[now].tag;
        tree[now << 1 | 1].tag ^= tree[now].tag;
        tree[now << 1].t = tree[now << 1 | 1].t = nowt;
    }
    if (tree[now].tag)
        swap(tree[now].cnt[0], tree[now].cnt[1]);
    tree[now].tag = 0;
    tree[now].sum[0] = tree[now].sum[1] = 0;
}
void pu(int now) {
    tree[now].cnt[0] = tree[now << 1].cnt[0] + tree[now << 1 | 1].cnt[0];
    tree[now].cnt[1] = tree[now << 1].cnt[1] + tree[now << 1 | 1].cnt[1];
    tree[now].ans = tree[now << 1].ans + tree[now << 1 | 1].ans;
}
void modify(int a, int b, int now = 1, int l = 1, int r = N + 1) {
    pd(now);
    if (r <= a || l >= b)
        return;
    if (r <= b && l >= a) {
        tree[now].tag = 1;
        pd(now);
        return;
    }
    int mid = (l + r) >> 1;
    modify(a, b, now << 1, l, mid);
    modify(a, b, now << 1 | 1, mid, r);
    pu(now);
}
LL query(int a, int b, int now = 1, int l = 1, int r = N + 1) {
    pd(now);
    if (r <= a || l >= b) {
        return 0;
    }
    if (r <= b && l >= a) {
        return tree[now].ans;
    }
    int mid = (l + r) >> 1;
    LL tmp = query(a, b, now << 1, l, mid) + query(a, b, now << 1 | 1, mid, r);
    return tmp;
}
int n, a[N], m;
int main() {
    rl(i, N + N - 1, 1) if (i >= N) tree[i].cnt[0] = 1;
    else tree[i].cnt[0] = tree[i << 1].cnt[0] << 1;
    scanf("%d", &n);
    rb(i, 1, n) scanf("%d", &a[i]);
    scanf("%d", &m);
    rb(i, 1, m) {
        int l, r;
        scanf("%d%d", &l, &r);
        que[r].PB(II(l, i));
    }
    rb(i, 1, n) {
        modify(pre[a[i]] + 1, i + 1);
        nowt++;
        for (auto it : que[i]) {
            ans[it.SEC] = query(it.FIR, i + 1);
        }
        pre[a[i]] = i;
    }
    rb(i, 1, m) printf("%lld\n", ans[i]);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM