VP Educational Codeforces Round 24


A. Diplomas and Certificates

点击查看代码
void solve() {
    i64 n, k;
    std::cin >> n >> k;
    i64 cnt = (n / 2) / (k + 1) * k;
    std::cout << cnt / k << " " << cnt << " " << n - cnt - cnt / k << "\n";
}

B. Permutation Game

题意:n个人围成一个圈,如果第i个人是领导者,那么下一个领导者是顺时针走ai个位置。其中a是一个排列。选择给出m轮的领导者,求a

可以通过li+1li得出ai,检查有没有冲突和重复,没有出现过的随便填没用过的数。

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

    std::vector<int> a(n + 1);
    std::set<int> s;
    for (int i = 1; i <= n; ++ i) {
    	s.insert(i);
    }

    for (int i = 1; i + 1 <= m; ++ i) {
    	int t = (l[i + 1] - l[i] + n) % n;
    	if (t == 0) {
    		t = n;
    	}

    	if (a[l[i]] == 0) {
    		if (!s.count(t)) {
    			std::cout << -1 << "\n";
    			return;
    		}

    		s.erase(t);
    		a[l[i]] = t;
    	} else if (a[l[i]] != t) {
    		std::cout << -1 << "\n";
    		return;
    	}
    }

    for (int i = 1; i <= n; ++ i) {
    	if (a[i] == 0) {
    		a[i] = *s.begin();
    		s.erase(s.begin());
    	}
    }

    for (int i = 1; i <= n; ++ i) {
    	std::cout << a[i] << " \n"[i == n];
    }
}

C. Sofa Thief

题意:n个沙发,每个沙发有两个坐标,对于i,j,如果j有一个横坐标小于i的一个横坐标,则ji的左边。如果j有一个横坐标大于i的一个横坐标,则ji的右边。如果j有一个纵坐标小于i的一个纵坐标,则ji的上边。如果j有一个纵坐标大于i的一个纵坐标,则ji的下边。
求有没有一个沙发的左边有cntl个沙发,右边有cntr个沙发,上面有cntt个沙发,下面有cntd个沙发。

给每个坐标从小到大从大到小排序模拟,求出每个沙发各个方向有多少沙发。

点击查看代码
void solve() {
    int k, n, m, L, R, T, D;
    std::cin >> k >> n >> m;
    std::vector<std::array<int, 3>> a;
    for (int i = 0; i < k; ++ i) {
    	int x1, y1, x2, y2;
    	std::cin >> x1 >> y1 >> x2 >> y2;
    	a.push_back({x1, y1, i});
    	a.push_back({x2, y2, i});
    }

    std::cin >> L >> R >> T >> D;

    std::vector<int> l(k, -1), r(k, -1), t(k, -1), d(k, -1);
    std::sort(a.begin(), a.end());
    std::set<int> s;
    for (int i = 0; i < a.size(); ++ i) {
    	int j = i;
    	while (j + 1 < a.size() && a[j + 1][0] == a[i][0]) {
    		++ j;
    	}

    	for (int x = i; x <= j; ++ x) {
    		l[a[x][2]] = (int)s.size() - s.count(a[x][2]);
    	}

    	for (int x = i; x <= j; ++ x) {
    		s.insert(a[x][2]);
    	}

    	i = j;
    }

    s.clear();

    std::sort(a.begin(), a.end(), std::greater<>());
    for (int i = 0; i < a.size(); ++ i) {
    	int j = i;
    	while (j + 1 < a.size() && a[j + 1][0] == a[i][0]) {
    		++ j;
    	}

    	for (int x = i; x <= j; ++ x) {
    		r[a[x][2]] = (int)s.size() - s.count(a[x][2]);
    	}

    	for (int x = i; x <= j; ++ x) {
    		s.insert(a[x][2]);
    	}

    	i = j;
    }

    s.clear();

    std::sort(a.begin(), a.end(), [&](std::array<int, 3> & a, std::array<int, 3> & b) {
    	return a[1] > b[1];
    });

    for (int i = 0; i < a.size(); ++ i) {
    	int j = i;
    	while (j + 1 < a.size() && a[j + 1][1] == a[i][1]) {
    		++ j;
    	}

    	for (int x = i; x <= j; ++ x) {
    		d[a[x][2]] = (int)s.size() - s.count(a[x][2]);
    	}

    	for (int x = i; x <= j; ++ x) {
    		s.insert(a[x][2]);
    	}

    	i = j;
    }

    s.clear();

    std::sort(a.begin(), a.end(), [&](std::array<int, 3> & a, std::array<int, 3> & b) {
    	return a[1] < b[1];
    });

    for (int i = 0; i < a.size(); ++ i) {
    	int j = i;
    	while (j + 1 < a.size() && a[j + 1][1] == a[i][1]) {
    		++ j;
    	}

    	for (int x = i; x <= j; ++ x) {
    		t[a[x][2]] = (int)s.size() - s.count(a[x][2]);
    	}

    	for (int x = i; x <= j; ++ x) {
    		s.insert(a[x][2]);
    	}

    	i = j;
    }

    s.clear();

    for (int i = 0; i < k; ++ i) {
    	// std::cout << l[i] << " " << r[i] << " " << t[i] << " " << d[i] << "\n";
    	if (l[i] == L && r[i] == R && t[i] == T && d[i] == D) {
    		std::cout << i + 1 << "\n";
    		return;
    	}
    }
    std::cout << -1 << "\n";
}

D. Multicolored Cars

题意:有一个数组,cntxix这个数在1i出现的次数。现在Alcie选了一个A,你要选一个B,使得对于任意一个i都有cntBicntAi

set存每个数的出现次数,然后从前往后更新cnt,同时更新set。然后每次把小于cntAi的位置都删掉。每次二分是log的,而每个数只会被删一次,总共是O(n)的,所以时间复杂度是O(nlogV)V是值域。

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

    std::set<std::pair<int, int>> s;
    for (int i = 1; i <= 1000000; ++ i) {
    	s.insert({0, i});
    }

    std::vector<int> cnt(1000010);
    for (auto & c : a) {
    	if (s.count({cnt[c], c})) {
    		s.erase({cnt[c], c});
	    	++ cnt[c];
    		s.insert({cnt[c], c});
	    }

	    auto it = s.lower_bound({cnt[A], 0});
	    s.erase(s.begin(), it);
    }

    s.erase({cnt[A], A});
    if (s.empty()) {
    	std::cout << -1 << "\n";
    } else {
    	std::cout << s.begin()->second << "\n";
    }
}

E. Card Game Again

题意:求有多少区间的乘积和是k的倍数。

k质因数分解,得到m个质数及其个数,然后记sum[i][j][1,i]里第j个质数出现的次数。那么我们枚举右端点,二分合法的最右边的左端点。

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

    std::map<int, int> mp;
    for (int i = 2; i <= k / i; ++ i) {
    	if (k % i == 0) {
    		while (k % i == 0) {
    			++ mp[i];
    			k /= i;
    		}
    	}
    }

    if (k > 1) {
    	++ mp[k];
    }

    std::vector<int> sumk;
    std::vector<int> b;
    for (auto & [x, cnt] : mp) {
    	b.push_back(x);
    	sumk.push_back(cnt);
    }

    int m = (int)b.size();

    std::vector sum(n + 1, std::vector<int>(m));
    for (int i = 1; i <= n; ++ i) {
    	sum[i] = sum[i - 1];
    	for (int j = 0; j < m; ++ j) {
    		int cnt = 0;
    		while (a[i] % b[j] == 0) {
    			++ cnt;
    			a[i] /= b[j];
    		}

    		sum[i][j] += cnt;
    	}
    }

    auto check = [&](int l, int r) -> bool {
    	for (int i = 0; i < m; ++ i) {
    		if (sum[r][i] - sum[l - 1][i] < sumk[i]) {
    			return false;
    		}
    	}

    	return true;
    };

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

		if (!check(l, i)) {
			l = 0;
		}

    	ans += l;
    }

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

F. Level Generation

题意:给你n个点,你要构造一个边数为m的图,使得图里的桥的数量m2。求m最大多少。

我们肯定是先用m2条边构造一棵树,然后其它点选树上一个点构成一个完全图。那么可以二分边数。

点击查看代码
void solve() {
	i64 n;
	std::cin >> n;
	auto check = [&](i64 m) -> bool {
		i64 cnt = (m + 1) / 2;
		return (n - cnt) * (n - cnt - 1) / 2 >= m - cnt;
	};

	if (n == 1) {
		std::cout << 0 << "\n";
		return;
	}

	i64 l = 1, r = 2 * n;
	while (l < r) {
		i64 mid = l + r + 1 >> 1ll;
		if (check(mid)) {
			l = mid;
		} else {
			r = mid - 1;
		}
	}

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

G. Four Melodies

题意:在一个数组种选四个不相交的子序列,每个子序列相邻的两个数要么相差1,要么余7相同。使得四个子序列长度加起来最大。

本质是在一个图里选k条路径使得路径和最大的问题。
如果我们向每个|aiaj|=1aiaj(mod7)(i,j)ij连边。那么就等价于求图里的两段路径,使得路径长度之和最大,这是个经典费用流问题。我们把每个点拆成出度和入度,一个点的入度向出度连费用为1流量为1的边,可以连边的由出度向入度连费用为0流量为1的边,同时源点给每个点的入度连费用为0流量为1的边,每个点的出度向汇点连费用为0流量为1的边,然后为了满足两条路径的需求,超级源点向源点连费用为0流量为2的边,汇点向超级汇点连费用为0流量为4的边。但这样边数太大,时间复杂度不能接受。
我们可以对于每个i的出度,往右边最近的aiaj=1以及aiaj=1j的入度连边,因为满足条件的aj值只有两种,我们选最近的可以给后面留出更大空间,显然更优。然后对于aiaj(mod7)的最近的j,我们由i的出度向j的入度连边,同时i的入度向j的入度连边,这意味着选了i可以继续选j,或者如果不选i下一个就可以选j。这样就解决了。
这个问题可以由4条路径扩展成k条路径,只需要改超级源点和超级汇点与源点汇点之间的流量。

点击查看代码
template<class T>
struct MinCostFlow {
    struct _Edge {
        int to;
        T cap;
        T cost;
        _Edge(int to_, T cap_, T cost_) : to(to_), cap(cap_), cost(cost_) {}
    };
    int n;
    std::vector<_Edge> e;
    std::vector<std::vector<int>> g;
    std::vector<T> h, dis;
    std::vector<int> pre;
    bool dijkstra(int s, int t) {
        dis.assign(n, std::numeric_limits<T>::max());
        pre.assign(n, -1);
        std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>, std::greater<std::pair<T, int>>> que;
        dis[s] = 0;
        que.emplace(0, s);
        while (!que.empty()) {
            T d = que.top().first;
            int u = que.top().second;
            que.pop();
            if (dis[u] != d) {
                continue;
            }
            for (int i : g[u]) {
                int v = e[i].to;
                T cap = e[i].cap;
                T cost = e[i].cost;
                if (cap > 0 && dis[v] > d + h[u] - h[v] + cost) {
                    dis[v] = d + h[u] - h[v] + cost;
                    pre[v] = i;
                    que.emplace(dis[v], v);
                }
            }
        }
        return dis[t] != std::numeric_limits<T>::max();
    }
    MinCostFlow() {}
    MinCostFlow(int n_) {
        init(n_);
    }
    void init(int n_) {
        n = n_;
        e.clear();
        g.assign(n, {});
    }
    void addEdge(int u, int v, T cap, T cost) {
        g[u].push_back(e.size());
        e.emplace_back(v, cap, cost);
        g[v].push_back(e.size());
        e.emplace_back(u, 0, -cost);
    }
    std::pair<T, T> flow(int s, int t) {
        T flow = 0;
        T cost = 0;
        h.assign(n, 0);
        while (dijkstra(s, t)) {
            for (int i = 0; i < n; ++i) {
                h[i] += dis[i];
            }
            T aug = std::numeric_limits<int>::max();
            for (int i = t; i != s; i = e[pre[i] ^ 1].to) {
                aug = std::min(aug, e[pre[i]].cap);
            }
            for (int i = t; i != s; i = e[pre[i] ^ 1].to) {
                e[pre[i]].cap -= aug;
                e[pre[i] ^ 1].cap += aug;
            }
            flow += aug;
            cost += aug * h[t];
        }
        return std::make_pair(flow, cost);
    }
    struct Edge {
        int from;
        int to;
        T cap;
        T cost;
        T flow;
    };
    std::vector<Edge> edges() {
        std::vector<Edge> a;
        for (int i = 0; i < e.size(); i += 2) {
            Edge x;
            x.from = e[i + 1].to;
            x.to = e[i].to;
            x.cap = e[i].cap + e[i + 1].cap;
            x.cost = e[i].cost;
            x.flow = e[i + 1].cap;
            a.push_back(x);
        }
        return a;
    }
};


void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    MinCostFlow<int> mf(2 * n + 4);
    int s = 2 * n, t = 2 * n + 1, S = 2 * n + 2, T = 2 * n + 3;
    mf.addEdge(S, s, 4, 0);
    mf.addEdge(t, T, 4, 0);

    for (int i = 0; i < n; ++ i) {
    	mf.addEdge(s, i, 1, 0);
    	mf.addEdge(i + n, t, 1, 0);
    	mf.addEdge(i, i + n, 1, -1);
    	for (int j = i + 1; j < n; ++ j) {
    		if (a[i] == a[j] + 1) {
    			mf.addEdge(i + n, j, 1, 0);
    			break;
    		}
    	}

    	for (int j = i + 1; j < n; ++ j) {
    		if (a[i] == a[j] - 1) {
    			mf.addEdge(i + n, j, 1, 0);
    			break;
    		}
    	}

    	for (int j = i + 1; j < n; ++ j) {
    		if (a[i] % 7 == a[j] % 7) {
    			mf.addEdge(i + n, j, 1, 0);
    			mf.addEdge(i, j, 1, 0);
    			break;
    		}
    	}
    }

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