Codeforces Round 1021 (Div. 2)


A. Vadim's Collection

题意:一个10位数,要求重排使得\(i\in [1, 10], a_i >= 10 - i\)。且字典序最小。

把每个数字出现次数存下来,然后每一位找大于等于它的第一个还没用完的数。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    int cnt[10]{};
    for (auto & c : s) {
    	++ cnt[c - '0'];
    }

    std::string ans;
    for (int i = 9; i >= 0; -- i) {
    	for (int j = i; j <= 9; ++ j) {
    		if (cnt[j]) {
    			ans += (char)(j + '0');
    			-- cnt[j];
    			break;
    		}
    	}
    }

    std::cout << ans << "\n";
}

B. Sasha and the Apartment Purchase

评价为难以看懂题目。

题意:\(n\)个点,你要在\([1, 10^9]\)里选点,一个点可以被选那么在删去不超过\(k\)个给定的点情况下,它是和剩下没被删去的给定点的距离和最小的。

先排序。
如果不删除点,那么根据中位数定理选\([a_{\frac{n}{2}}, a_{\lfloor \frac{n}{2} \rfloor}]\)之间的点是符合条件的。
考虑删去\(k\)个,那么肯定是删去两边的点。那么我们可以枚举每个长度为\(n - k\)的区间,奇数来\(n - k + 1\)个区间,然后把题目合并。就可以计算点数。但这样还是太麻烦了,发现这些长度为\(n-k\)的区间,相邻的两个区间的中位数的位置最多移动一位,那么这些区间肯定是相交的。那么直接计算最左边区间的中位数的位置,和最右边的位置就行了。注意最左边的区间要选靠左的中位数,最右边的区间选靠右的中位数。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::ranges::sort(a);
    k = n - k;
    int l = (k - 1) / 2, r = (n - 1 + (n - 1 - k + 1) + 1) / 2;

    std::cout << a[r] - a[l] + 1 << "\n";
}


C. Sports Betting

题意:和\(n\)个人打赌,每一天要么是\(0\)要么是\(1\),和第\(i\)个人在第\(i\)天打赌,告诉他就行了两天的情况。求能不能至少赢一个人。

如果第\(i\)天和两个人打赌,那么可以分别预测是\(01, 10\)这两个,那么第\(i + 1\)天总会有一个符合条件,但只要第\(i + 2\)不是堵的那个就一样数,如果第\(i + 2\)还能和两个人打赌,那么假设第\(i+1\)天是\(0\),那么直接预测\(11, 10\)这两个,那么如果第\(i + 2\)天是\(0\),则赢了第\(i\)天的打赌,否则第\(i+2\)天是\(1\),我们已经预处第\(i+2\)\(0\)的所有情况了,会赢第\(i+1\)天的打赌。如果第\(i+1\)天只有一个人打赌,那么同样的打赌接下来两天是\(01, 10\)这两个,这样一直下去,只要连续的天数是大于等于\(2\)个人开头,中间若干个\(1\),然后是大于等于\(2\)个人结尾,就能赢。
如果有\(4\)个以上在同一天也是赢。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::map<int, int> mp;
    for (auto & x : a) {
    	++ mp[x];
    }

    std::vector<std::pair<int, int>> b;
    for (auto & [x, y] : mp) {
    	b.emplace_back(x, y);
    }

    int m = b.size();
    for (int i = 0; i < m; ++ i) {
    	if (b[i].second >= 4) {
    		std::cout << "YES\n";
    		return;
    	}
    }

    for (int i = 0; i < m; ++ i) {
    	if (b[i].second >= 2) {
    		int j = i;
    		while (j + 1 < m && b[j + 1].first - 1 == b[j].first) {
    			++ j;
    			if (b[j].second >= 2) {
	    			std::cout << "YES\n";
	    			return;
	    		}
    		}

    		
    		i = j;
    	}
    }
    std::cout << "NO\n";
}

D. Baggage Claim

题意:一个\(n\times m\)的矩阵,有一个\(2\times k + 1\)的路径,没有一个点经过两次以上。现在给出路径奇数位的位置,求合法的路径数。

首先如果两个点的曼哈顿距离不是\(2\)无解。
否则我们可以建图,发现两个点直接可以选择的点要么是1个要么是2个,如果两个点某个坐标相同,则只有一种选法,否则有两种,但可能有些点被多个点选择。所以我们把只有一种选法的看作两个相同的点,然后对于两个点连边,两个点有边意味着必须在两个点里选一个。同时以给出的奇数点是必须的,它这个点到这个点连一条边。
那么就组成了多个联通块,每个联通块的方案数乘起来就行了。
对于一个联通块,如果边数大于点数,显然没有合适的选法,答案直接是\(0\)
如果边数等于点数,那么如果这个联通块是一个环,则有两种选法,因为两个点直接最多两个点选择,选择其中一个其它的也就确定了。如果不是一个环,意味着有些点自己连自己的了,那么方案数是\(1\)
如果边数等于点数减一,则随便选一个点就确定了方案,方案数是点数个数。

点击查看代码
struct DSU {
    std::vector<int> fa, cnt;
    DSU(int _n) {
        init(_n);
    }

    void init(int _n) {
        fa.assign(_n, 0);
        cnt.assign(_n, 1);
        std::iota(fa.begin(), fa.end(), 0);
    }

    int find(int x) {
        return x == fa[x] ? x : fa[x] = find(fa[x]);
    }

    bool merge(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) {
            return false;
        }

        fa[y] = x;
        cnt[x] += cnt[y];
        return true;
    }

    bool same(int x, int y) {
        return find(x) == find(y);
    }

    int size(int x) {
        return cnt[find(x)];
    }
};

const int mod = 1e9 + 7;

void solve() {
    int n, m, k;
    std::cin >> n >> m >> k;
    std::vector<int> x(k + 1), y(k + 1);
    for (int i = 0; i <= k; ++ i) {
        std::cin >> x[i] >> y[i];
        -- x[i], -- y[i];
    }

    auto get = [&](int i, int j) -> int {
        return i * m + j;
    };

    DSU dsu(n * m);
    std::vector<int> cnt(n * m);
    std::vector<int> st(n * m);
    for (int i = 0; i <= k; ++ i) {
        cnt[get(x[i], y[i])] = 1;
        st[get(x[i], y[i])] = 1;
    }

    for (int i = 0; i + 1 <= k; ++ i) {
        if (std::abs(x[i] - x[i + 1]) + std::abs(y[i] - y[i + 1]) != 2) {
            std::cout << 0 << "\n";
            return;
        }

        int x1, y1, x2, y2;
        if (x[i] == x[i + 1]) {
            x1 = x2 = x[i];
            y1 = y2 = y[i] + y[i + 1] >> 1;
        } else if (y[i] == y[i + 1]) {
            y1 = y2 = y[i];
            x1 = x2 = x[i] + x[i + 1] >> 1;
        } else {
            x1 = x[i + 1], y1 = y[i];
            x2 = x[i], y2 = y[i + 1];
        }

        int u = get(x1, y1), v = get(x2, y2);
        if (!dsu.same(u, v)) {
            cnt[dsu.find(u)] += cnt[dsu.find(v)];
            st[dsu.find(u)] |= st[dsu.find(v)];
            dsu.merge(u, v);
        }   

        cnt[dsu.find(u)] += 1;
        if (u == v) {
            st[dsu.find(u)] = 1;
        }
    }    

    int ans = 1;
    for (int i = 0; i < n * m; ++ i) {
        if (dsu.find(i) == i) {
            int cnt1 = dsu.size(i);
            if (cnt1 < cnt[i]) {
                ans = 0;
            } else if (cnt1 == cnt[i]) {
                if (!st[i]) {
                    ans = (i64)ans * 2 % mod;
                }
            } else {
                ans = (i64)ans * cnt1 % mod;
            }
        }
    }
    std::cout << ans << "\n";
}
posted @ 2025-04-27 02:55  maburb  阅读(912)  评论(0)    收藏  举报