2023.1.14

A.Factory Balls

https://codeforces.com/group/sg0CXKHVzz/contest/421181/problem/A

Statement

给出 \(n \ (1 \leq n \leq 10)\) 个编号为 \([1, n]\) 的点,初始每个点的颜色都为 \(1\)。有 $m (0 \leq m \leq 10) $ 种装备,每种装备会遮住一些位置(不一定是连续区间)。每一个时刻可以进行如下操作之一:
1.选择一种颜色 \(i \ (1 \leq i \leq k, 1 \leq k \leq 10)\),将所有没有被装备遮住的位置染上颜色 \(i\)
2.选择一个没有装上的装备装上它。
3.卸下一个装备。
给出每个位置最后的颜色 \(c_i\),询问是否能达到最终状态(并且没有装任何装备),若可以则输出最少步数。

Solution

观察到数据范围后考虑搜索所有的状态,从中求得最小步数。 若枚举每个位置的所有颜色,总状态数将变成 \(k ^ n \cdot 2 ^m\),过于庞大。不难发现其实不用关注每个区域的具体颜色,只用关心是否染上了最终状态的颜色即可。因此在 bfs 时记录两个二进制串,第一个串长度为 \(n\),其第 \(i\) 位为 \(1\) 表示第 \(i\) 块的颜色是最终颜色,为 \(0\) 表示是其他颜色;第二个串长度为 \(m\) ,其第 \(i\) 位为 \(1\) 表示第 \(i\) 种装备已被使用,为 \(0\) 表示未被使用。这样被枚举到的状态数则优化到了 \(2 ^ {n + m} \cdot (nk + m)\)

Code

#include <bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
const int lim = 1 << 12;
const int N = 20;
using namespace std;

int n, k, m, x, t, a[N], f[lim][lim];
bool vis[N];
vector<int> eq[N];
queue<pair<int, int> > q;

void bfs(int state) {
    memset(f, 0x3f, sizeof(f));
    f[state][0] = 0;
    q.push(mp(state, 0));
    while (!q.empty()) {
        auto now = q.front();
        q.pop();
        int sta1 = now.fi, sta2 = now.se;

        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= m; i++) {
            if ((sta2 >> (i - 1)) & 1) {
                for (auto j : eq[i]) vis[j] = 1;
            }
        }
        for (int c = 1; c <= k; c++) {
            int res1 = sta1;
            for (int p = 1; p <= n; p++) {
                if (vis[p]) continue;
                if (a[p] == c) {
                    res1 |= (1 << (p - 1));
                    continue;
                }
                if ((res1 >> (p - 1)) & 1) res1 ^= (1 << (p - 1)); 
            }
            if (f[res1][sta2] > f[sta1][sta2] + 1) {
                f[res1][sta2] = f[sta1][sta2] + 1;
                q.push(mp(res1, sta2));
            }
        }

        for (int i = 1; i <= m; i++) {
            int res2 = sta2 ^ (1 << (i - 1));
            if (f[sta1][res2] > f[sta1][sta2] + 1) {
                f[sta1][res2] = f[sta1][sta2] + 1;
                q.push(mp(sta1, res2));
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    freopen("in","r", stdin);
    cin >> n >> k >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= m; i++) {
        cin >> t;
        for (int j = 1; j <= t; j++) {
            cin >> x;
            eq[i].push_back(x);
        }
    }
    int state = 0, fina = (1 << n) - 1;
    for (int i = 1; i <= n; i++) state |= (a[i] == 1) ? (1 << (i - 1)) : 0;
    bfs(state);
    f[fina][0] == 0x3f3f3f3f ? cout << "-1" << "\n" : cout << f[fina][0] << "\n";
    return 0;
}

B.Triple Sword Strike

https://codeforces.com/gym/103855/problem/D

Statement

二维平面上有一些不重复的点 \((0 \leq x,y \leq 10 ^ 6)\),每个点上还有一个权值。每次你可以选择一条平行于 \(x\) 轴或者 \(y\) 轴的直线,选择后你将收获这条线穿过的点的权值的和,这些点在被第一次穿过后也将消失。你现在可以选择 \(3\) 条这样的直线,最大化你收获到的权值和。

Solution

点的坐标不大,可以直接作为下标用来统计横坐标为 \(x\) 的点的集合和他们的和(纵坐标为 \(y\) 的同理)。对 \(3\) 条直线分类讨论:三横、三竖、两横一竖、两竖一横。前两种情况直接选择和前三大的即可,后面两种情况可以枚举哪条线上的点被算重,查找删除更新,在 multiset 中维护即可。

Code

#include <bits/stdc++.h>
typedef long long ll;
const int N = 3e6 + 5;
const int lim = 1e6;
using namespace std;

multiset<ll, greater<ll> > sx, sy;
vector<int> xx[lim + 5], yy[lim + 5];
int n, x[N], y[N], v[N];
ll val_x[lim + 5], val_y[lim + 5];

void solve1(ll &ans) {
    for (int i = 0; i <= lim; i++)
        if (val_x[i]) sx.insert(val_x[i]);
    int num1 = 0;
    ll res1 = 0;
    for (auto c : sx) {
        ++num1;
        res1 += c;
        if (num1 == 3) break;
    }
    ans = max(ans, res1);

    for (int i = 0; i <= lim; i++)
        if (val_y[i]) sy.insert(val_y[i]);
    int num2 = 0;
    ll res2 = 0;
    for (auto c : sy) {
        ++num2;
        res2 += c;
        if (num2 == 3) break;
    }
    ans = max(ans, res2);
}

void solve2(ll &ans) {
    for (int i = 0; i <= lim; i++) {
        if (!xx[i].size()) continue;
        ll sum1 = val_x[i];
        for (auto tar : xx[i]) {
             auto it = sy.find(val_y[y[tar]]);
             sy.erase(it);
             val_y[y[tar]] -= v[tar];
             sy.insert(val_y[y[tar]]);
        }
        int num2 = 0;
        for (auto c : sy) {
            ++num2;
            sum1 += c;
            if (num2 == 2) break;
        }
        ans = max(ans, sum1);
        for (auto tar : xx[i]) {
             auto it = sy.find(val_y[y[tar]]);
             sy.erase(it);
             val_y[y[tar]] += v[tar];
             sy.insert(val_y[y[tar]]);
        }
    }

    for (int i = 0; i <= lim; i++) {
        if (!yy[i].size()) continue;
        ll sum2 = val_y[i];
        for (auto tar : yy[i]) {
             auto it = sx.find(val_x[x[tar]]);
             sx.erase(it);
             val_x[x[tar]] -= v[tar];
             sx.insert(val_x[x[tar]]);
        }
        int num1 = 0;
        for (auto c : sx) {
            ++num1;
            sum2 += c;
            if (num1 == 2) break;
        }
        ans = max(ans, sum2);
        for (auto tar : yy[i]) {
             auto it = sx.find(val_x[x[tar]]);
             sx.erase(it);
             val_x[x[tar]] += v[tar];
             sx.insert(val_x[x[tar]]);
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    freopen("in","r", stdin);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> x[i] >> y[i] >> v[i];
        xx[x[i]].push_back(i); val_x[x[i]] += v[i];
        yy[y[i]].push_back(i); val_y[y[i]] += v[i];
    }
    ll ans = 0;
    solve1(ans); solve2(ans);
    cout << ans << "\n";
    return 0;
}

C.Short Question

给定 \(n(1 \leq n \leq 10 ^ 6)\) 以及 \(p_1, p_2, \cdots p_n\)\(q_1, q_2, \cdots q_n\),计算下式的值:

\[\sum_{i = 1} ^ {n} \sum_{j = 1} ^ n min(|p_i - p_j|, |q_i - q_j|) \]

Solution

带有min以及max这类的式子难以求值,考虑将其拆开。一个显然且重要的结论是 \(min(x, y) = x + y - max(x, y)\),利用该式子可以化简:

\[min(|p_i - p_j|, |q_i - q_j|) = \\ |p_i - p_j| + |q_i - q_j| - max(|p_i - p_j|, |q_i - q_j|) \]

若将 \(p_i, p_j, q_i, q_j\) 看作二维平面上的点的坐标,上式可以等效为求二维平面上所有点对之间的 曼哈顿距离 减去 切比雪夫距离 的和。

曼哈顿距离与切比雪夫距离的转换问题比较常见,不再赘述。常用的 tips 是:

\((x, y)\) ---> \((x + y, x - y)\),此时原坐标系中的曼哈顿距离 = 新坐标系中的切比雪夫距离。

\((x, y)\) ---> \((\frac {x + y} {2}, \frac {x - y}{2})\),此时原坐标系中的切比雪夫距离 = 新坐标系中的曼哈顿距离。

因此上式中的切比雪夫距离部分可以也可以转换为求曼哈顿距离中 sigma 绝对值的形式来计算。

考虑如何算 \(|p_i - p_j|\):由于 \(x, y\) 独立,可以把 \(p_i\) 进行升序排序,这样可以省去绝对值的运算。具体而言,\(p_i\) 始终大于前 \(i - 1\) 个的 \(p_j\),贡献为正,且始终小于后 \(n - i\) 个的 \(p_j\),贡献为负。因此:

\[\sum _{i = 1} ^ n \sum _{j = 1} ^ n |p_i - p_j| = 2 \cdot \sum _{i = 1} ^ n ((i - 1) - (n - i)) \cdot p_i \]

对以上绝对值分别求值后相加即可。

#include <bits/stdc++.h>
const int N = 1e6 + 5;
using namespace std;

int n, p[N], q[N], np[N], nq[N];
long long ans1, ans2;

int main() {
    ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	freopen("in","r", stdin);
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> p[i];
    for (int i = 1; i <= n; i++) cin >> q[i];
    for (int i = 1; i <= n; i++) {
        np[i] = (p[i] + q[i]);
        nq[i] = (p[i] - q[i]);
    }
    sort (p + 1, p + n + 1);
    sort (q + 1, q + n + 1);
    sort (np + 1, np + n + 1);
    sort (nq + 1, nq + n + 1);
    for (int i = 1; i <= n; i++) ans1 += 1ll * ((i - 1) - (n - i)) * p[i];
    for (int i = 1; i <= n; i++) ans1 += 1ll * ((i - 1) - (n - i)) * q[i];  
    for (int i = 1; i <= n; i++) ans2 += 1ll * ((i - 1) - (n - i)) * np[i];
    for (int i = 1; i <= n; i++) ans2 += 1ll * ((i - 1) - (n - i)) * nq[i];
    cout << 2 * ans1 - ans2 << endl;
	return 0;
}
posted @ 2023-01-16 12:52  BeyondLimits  阅读(25)  评论(0编辑  收藏  举报