VP Codeforces Round 905 (Div. 2)


A. Chemistry

题意:一个字符串,能不能删除不超过k字符和重排后是回文。

记录每个字符的数量,如果是奇数则我们可能需要删除,最多留一个不能删的放在中间。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::string s;
    std::cin >> s;
    std::vector<int> cnt(26);
    for (auto & c : s) {
    	++ cnt[c - 'a'];
    }

    int tot = 0;
    for (int i = 0; i < 26; ++ i) {
    	tot += cnt[i] & 1;
    }

    if (k - tot >= -1) {
    	std::cout << "YES\n";
    } else {
    	std::cout << "NO\n";
    }
}

B. Raspberries

题意:给你一个数组,每次可以给一个数加一,使得这些数的乘积是k的倍数,问最少加几次。k[2,5]

发现除了k等于4的时候,都是质数,只需要求每个数变成k的倍数需要加的最小值。如果是4,则还要看选两个数变成2的倍数需要加多少。

点击查看代码
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];
    }

    int ans = 1e9;
    for (int i = 0; i < n; ++ i) {
    	ans = std::min(ans, (a[i] + k - 1) / k * k - a[i]);
    }

    if (k == 4) {
    	int min1 = 1e9, min2 = 1e9;
    	for (int i = 0; i < n; ++ i) {
    		int x = (a[i] + 1) / 2 * 2 - a[i];
    		if (x < min1) {
    			min2 = min1;
    			min1 = x;
    		} else if (x < min2) {
    			min2 = x;
    		}
    	}

    	std::cout << std::min(ans, min1 + min2) << "\n";
    } else {
    	std::cout << ans << "\n";
    }
}

C. You Are So Beautiful

题意:求数组有多少子数组在所有子序列里只出现过一次。

预处理每个数前面有没有出现过,和在后面有没有出现过。那么所选的子数组的第一个数和最后一个数在前后各自不能出现。从前往后枚举右端点,如果右端点合法则加上前面合法的左端点的个数。

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

    std::vector<int> pre(n), suf(n);
    std::set<int> s;
    for (int i = 0; i < n; ++ i) {
    	if (s.count(a[i])) {
    		pre[i] = 1;
    	}

    	s.insert(a[i]);
    }

    s.clear();
    for (int i = n - 1; i >= 0; -- i) {
    	if (s.count(a[i])) {
    		suf[i] = 1;
    	}

    	s.insert(a[i]);
    }

    i64 ans = 1;
    int sum = 0;
    for (int i = 0; i < n; ++ i) {
    	sum += !pre[i];
    	if (!suf[i]) {
    		ans += i == n - 1 ? sum - 1 : sum;
    	}
    }

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

D1. Dances (Easy version) && D2. Dances (Hard Version)

题意:给你两个数组a,b其中a的第一个数待定。求a1[1,m]的每种情况的最小操作数。两个数组满足条件使得两个数组各自删除同样个数的数,然后重排后使得ai<bi

因为可以重排,我们自然是让两个数组都从小到大排。然后a删去前面一些数,b删去后面一些数。那么对于D1m=1,我们只需要二分出最小操作数。
对于D2,我们先看m作为a1的取值范围为[1,a2],那么[1,b11]的操作数是相同的,我们二分求出后面第一个和它操作数相同的数,对于m作为ai取值范围为[ai1,ai],那么[ai1,bi1]的操作数是相同的,同样可以二分求。于是我们从m=1开始,每次二分求与它操作数相等的数j,然后i=j+1这样一直跳即可。发现最多跳log2m次。

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

    for (int i = 0; i < n; ++ i) {
    	std::cin >> b[i];
    }

    std::sort(b.begin(), b.end());

    auto get = [&](int x) -> int {
        auto aa = a;
        aa[0] = x;
        std::sort(aa.begin(), aa.end());
        auto check = [&](int x) -> bool {
        	for (int i = 0; i < n - x; ++ i) {
        		if (aa[i] >= b[i + x]) {
        			return false;
        		}
        	}

        	return true;
        };

        int l = 0, r = n;
        while (l < r) {
        	int mid = l + r >> 1;
        	if (check(mid)) {
        		r = mid;
        	} else {
        		l = mid + 1;
        	}
        }

        return l;
    };

    i64 ans = 0;
    for (int i = 1; i <= m;) {
        int l = i, r = m;
        int t = get(i);
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (t == get(mid)) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }

        ans += (i64)(l - i + 1) * get(i);
        // std::cout << get(i) << " " << i << " " << l << "\n";
        i = l + 1;
    }

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

E. Time Travel

题意:有t个图,每个图有n个点,mi条边。同时给出一个序列,每次你会在第ai个图上操作,你每次可以选择走一条边或者不动。你一开始在第a1个图的1号点,你要到第n号点,求最少几次操作后能到。

对于第i个图的边(u,v),连上(u,v,i)的边,代表这条边是第几个图上的。那么我们可以dijkstra,记录distu为到u时的最少操作数。那么我们给每个图在序列里出现的位置存下来,那么对于一条边(u,v,i),我们二分第i张图大于等于distu这个位置的最小的位置,就可以转移。

点击查看代码
void solve() {
    int n, t;
    std::cin >> n >> t;
    std::vector<std::vector<std::pair<int, int>>> adj(n);
    for (int i = 0; i < t; ++ i) {
    	int m;
    	std::cin >> m;
    	while (m -- ) {
    		int u, v;
    		std::cin >> u >> v;
    		-- u, -- v;
    		adj[u].push_back({v, i});
    		adj[v].push_back({u, i});
    	}
    }

    int k;
    std::cin >> k;
    std::vector<std::vector<int>> pos(t);
    for (int i = 0; i < k; ++ i) {
    	int x;
    	std::cin >> x;
    	-- x;
    	pos[x].push_back(i);
    }

    const int inf = 1e9;
    using PII = std::pair<int, int>;
    std::vector<int> dist(n, inf);
    dist[0] = 0;
    std::priority_queue<PII, std::vector<PII>, std::greater<PII>> heap;
    heap.push({dist[0], 0});
    while (heap.size()) {
    	auto [d, u] = heap.top(); heap.pop();
    	if (d != dist[u]) {
    		continue;
    	}

    	for (auto & [v, t] : adj[u]) {
    		auto it = std::lower_bound(pos[t].begin(), pos[t].end(), d);
    		if (it == pos[t].end()) {
    			continue;
    		}

    		if (dist[v] > *it + 1) {
    			dist[v] = *it + 1;
    			heap.push({dist[v], v});
    		}
    	}
    }

    int ans = dist[n - 1] == inf ? -1 : dist[n - 1];
    std::cout << ans << "\n";
}

F. Minimum Array

题意:一个数组,m次区间加操作,求第几次操作后的数组在所有数组里字典序最小。

区间加可以联想到差分操作。我们用mapd数组,那么每次操作后,先把最前面的加0的位置删掉,然后看第一个不为0的位置是不是加的负数,如果加的负数,意味着数组的字典序变小了,记录操作到了第几个,然后清空map,继续操作,这样下一个字典序小的就是相当于当前字典序最小的数组而言的,就是前i个数组字典序最小的。

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

    int m;
    std::cin >> m;
    std::vector<int> l(m + 1), r(m + 1), x(m + 1);
    int p = 0;
    std::map<int, i64> d;
    for (int i = 1; i <= m; ++ i) {
    	std::cin >> l[i] >> r[i] >> x[i];
    	d[l[i]] += x[i], d[r[i] + 1] -= x[i];
   		while (!d.empty() && d.begin()->second == 0) {
   			d.erase(d.begin());
   		} 

   		if (!d.empty() && d.begin()->second < 0) {
   			p = i;
   			d.clear();
   		}
    }

    std::vector<i64> b(n + 2);
    for (int i = 1; i <= p; ++ i) {
    	b[l[i]] += x[i]; b[r[i] + 1] -= x[i];
    }

    for (int i = 1; i <= n; ++ i) {
    	b[i] += b[i - 1];
    	a[i] += b[i];
    }

    for (int i = 1; i <= n; ++ i) {
    	std::cout << a[i] << " \n"[i == n];
    }
}
posted @   maburb  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示