VP Educational Codeforces Round 21


A. Lucky Year

题意:找比n大的下一个只有一位数不是0的数。

先和n同位数,然后高位一直加一直到大于n

点击查看代码
void solve() {
    i64 n;
    std::cin >> n;
    i64 x = 1;
    while (x * 10 <= n) {
    	x *= 10;
    }

    i64 m = x;
    while (m <= n) {
    	m += x;
    }

    std::cout << m - n << "\n";
}

B. Average Sleep Time

题意: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::vector<i64> sum(n + 1);
    for (int i = 0; i < n; ++ i) {
    	sum[i + 1] = sum[i] + a[i];
    }

    double ans = 0;
    for (int i = k; i <= n; ++ i) {
    	ans += sum[i] - sum[i - k];
    }

    std::cout << std::fixed << std::setprecision(12);
    std::cout << ans / (n - k + 1) << "\n";
}

C. Tea Party

题意:构造一个长度为n的序列b,使得i=1nbi=mi[1,n],biai2i,j[1,n] if aiaj then biaj

先满足biai2,然后按照ai从大到小放,能放多少就放多少。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<std::pair<int, int>> a(n);
    std::vector<int> ans(n);
    for (int i = 0; i < n; ++ i) {
    	int x;
    	std::cin >> x;
    	a[i] = {x, i};
    	ans[i] = (x + 1) / 2;
    	m -= (x + 1) / 2;
    }

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

    std::sort(a.begin(), a.end(), std::greater<>());
    for (auto & [x, i] : a) {
    	int v = std::min(m, x - (x + 1) / 2);
    	ans[i] += v;
    	m -= v;
    }

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

D. Array Division

题意:给你一个数组,移动一个数,使得数组的一个前缀的和与一个后缀的和相等。

只能移动一个,考虑移动哪一个很困难,可以直接枚举移动这个数影响的前缀和后缀,如果当前前缀为pre,则如果pre>sum2需要从前面找一个sum2pre移动后面,否则pre<sum2,需要从后面找一个sum2pre到前面。如果pre=sum2就直接yes。用map记录两边的数。

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

    i64 sum = std::accumulate(a.begin(), a.end(), 0ll);
    if (sum & 1) {
    	std::cout << "NO\n";
    	return;
    }

    i64 x = sum / 2;
    sum = 0;
    for (int i = 0; i < n; ++ i) {
    	sum += a[i];
    	pre[a[i]] += 1; suf[a[i]] -= 1;
    	if (sum == x) {
    		std::cout << "YES\n";
    		return;
    	} else if (sum < x && suf[x - sum] > 0) {
    		std::cout << "YES\n";
    		return;
    	} else if (sum > x && pre[sum - x] > 0) {
    		std::cout << "YES\n";
    		return;
    	}
    }

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

E. Selling Souvenirs

题意:一个背包,n,m都是1e5级别,但物品的体积只有{1,2,3}三种。求最大价值。

以前没写过这种类似贪心的dp
每个物品按体积分类,然后按价值从大到小排序,那么我们肯定是选每一类的一个前缀。我们先只管体积为12的。记f[i]={cost,cnt1,cnt2},表示总体积为i时最大价值为cost,且取到这个价值时选了cnt11cnt22。然后转移就显然,因为我们知道了选了几个1和几个2,直接枚举拿一个1,拿两个1和拿一个2的情况转移就行了。
我感觉这个记录选几个12的状态就比较贪心,不管仔细想一下,这其实就是f[i][j][k]表示总体积为i取到这个价值时选了j1k2时最大价值,压了两维,然后只记录最优方案,因为我们只需要最优解,其它的状态可以丢弃。感觉还是很奇妙,以前写dp都是一个状态一维,有时利用一些性质减掉几维,但确实没见过把状态和值放到一起的。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<std::vector<i64>> a(4);
    for (int i = 0; i < n; ++ i) {
    	int w, c;
    	std::cin >> w >> c;
    	a[w].push_back(c);
    }

    for (int i = 1; i <= 3; ++ i) {
    	std::sort(a[i].begin(), a[i].end(), std::greater<>());
    	while (a[i].size() <= 2 * m) {
    		a[i].push_back(0);
    	}
    }

    std::vector<std::array<i64, 3>> f(m + 2);
    for (int i = 0; i < m; ++ i) {
    	if (f[i + 1][0] < f[i][0] + a[1][f[i][1]]) {
    		f[i + 1][0] = f[i][0] + a[1][f[i][1]];
    		f[i + 1][1] = f[i][1] + 1;
    		f[i + 1][2] = f[i][2];
    	}

    	if (f[i + 2][0] < f[i][0] + a[1][f[i][1]] + a[1][f[i][1] + 1]) {
    		f[i + 2][0] = f[i][0] + a[1][f[i][1]] + a[1][f[i][1] + 1];
    		f[i + 2][1] = f[i][1] + 2;
    		f[i + 2][2] = f[i][2];
    	}

		if (f[i + 2][0] < f[i][0] + a[2][f[i][2]]) {
    		f[i + 2][0] = f[i][0] + a[2][f[i][2]];
    		f[i + 2][1] = f[i][1];
    		f[i + 2][2] = f[i][2] + 1;
    	}    	
    }

    i64 ans = 0, pre = 0;
    for (int i = 0; i <= m / 3; ++ i) {
    	ans = std::max(ans, f[m - i * 3][0] + pre);
    	pre += a[3][i];
    }

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

F. Card Game

题意:有n个三元组(p,c,l),如果你的等级大于等于l则可以选这个三元组,如果两个三元组的ci+cj是质数,则这两个不能同时选,选的三元组的价值就是p和,你想要和大于等于k,问等级最少是多少。

我们把奇数放一起,偶数放一起,就变成了二部图,然后如果两个点的和是质数就连边,这样就变成了求最大独立集。
1需要特殊处理,因为1+1是质数,但我们最多选一个1,于是我们保留p最大的那个1就行。
可以二分等级,也可以一级一级增加,然后把对应等级的三元组加进来和已有的连边。因为我们是在残留网络上跑增广路,复杂度是可以接受的。

点击查看代码
constexpr int inf = 1E9;
template<class T>
struct MaxFlow {
    struct _Edge {
        int to;
        T cap;
        _Edge(int to, T cap) : to(to), cap(cap) {}
    };
    
    int n;
    std::vector<_Edge> e;
    std::vector<std::vector<int>> g;
    std::vector<int> cur, h;
    
    MaxFlow() {}
    MaxFlow(int n) {
        init(n);
    }
    
    void init(int n) {
        this->n = n;
        e.clear();
        g.assign(n, {});
        cur.resize(n);
        h.resize(n);
    }
    
    bool bfs(int s, int t) {
        h.assign(n, -1);
        std::queue<int> que;
        h[s] = 0;
        que.push(s);
        while (!que.empty()) {
            const int u = que.front();
            que.pop();
            for (int i : g[u]) {
                auto [v, c] = e[i];
                if (c > 0 && h[v] == -1) {
                    h[v] = h[u] + 1;
                    if (v == t) {
                        return true;
                    }
                    que.push(v);
                }
            }
        }
        return false;
    }
    
    T dfs(int u, int t, T f) {
        if (u == t) {
            return f;
        }
        auto r = f;
        for (int &i = cur[u]; i < int(g[u].size()); ++i) {
            const int j = g[u][i];
            auto [v, c] = e[j];
            if (c > 0 && h[v] == h[u] + 1) {
                auto a = dfs(v, t, std::min(r, c));
                e[j].cap -= a;
                e[j ^ 1].cap += a;
                r -= a;
                if (r == 0) {
                    return f;
                }
            }
        }
        return f - r;
    }
    void addEdge(int u, int v, T c) {
        g[u].push_back(e.size());
        e.emplace_back(v, c);
        g[v].push_back(e.size());
        e.emplace_back(u, 0);
    }
    T flow(int s, int t) {
        T ans = 0;
        while (bfs(s, t)) {
            cur.assign(n, 0);
            ans += dfs(s, t, std::numeric_limits<T>::max());
        }
        return ans;
    }
    
    std::vector<bool> minCut() {
        std::vector<bool> c(n);
        for (int i = 0; i < n; i++) {
            c[i] = (h[i] != -1);
        }
        return c;
    }
    
    struct Edge {
        int from;
        int to;
        T cap;
        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.flow = e[i + 1].cap;
            a.push_back(x);
        }
        return a;
    }
};


const int N = 2e5 + 5;

std::vector<int> primes;
bool st[N];
void init(int n) {
	for (int i = 2; i <= n; ++ i) {
		if (!st[i]) {
			primes.push_back(i);
		}

		for (auto & p : primes) {
			if (p * i > n) {
				break;
			}

			st[p * i] = true;
			if (i % p == 0) {
				break;
			}
		}
	}
}

void solve() {
	init(2e5);
    int n, k;
    std::cin >> n >> k;
    std::vector<std::vector<std::array<int, 3>>> a(N);
    for (int i = 0; i < n; ++ i) {
    	int p, c, l;
    	std::cin >> p >> c >> l;
    	a[l].push_back({p, c, i});
    }

    i64 flow = 0, sum = 0, one_id = n + 2, one_w = 0;;
    MaxFlow<i64> f(n + 3);
    int s = n, t = n + 1;
    std::vector<std::array<int, 3>> b;
    for (int i = 1; i < N; ++ i) {
    	for (auto & [px, cx, x] : a[i]) {
    		sum += px;
    		if (cx == 1) {
    			sum -= px;
    			if (px > one_w) {
    				sum -= one_w;
    				sum += px;
    				f.addEdge(s, one_id, px - one_w);
	    			one_w = px;
    			}
    		} else if (cx & 1) {
    			f.addEdge(s, x, px);
    		} else {
    			f.addEdge(x, t, px);
    		}
    		for (auto & [py, cy, y] : b) {
    			if (!st[cx + cy] && cx != cy) {
    				if (cx & 1) {
    					if (cx == 1) {
	    					f.addEdge(one_id, y, inf);
    					} else {
    						f.addEdge(x, y, inf);
    					}	
    				} else {
    					if (cy == 1) {
    						f.addEdge(one_id, x, inf);
    					} else {
    						f.addEdge(y, x, inf);
    					}
    				}
    			}
    		}

    		b.push_back({px, cx, x});
    	}

    	flow += f.flow(s, t);
    	if (sum - flow >= k) {
    		std::cout << i << "\n";
    		return;
    	}
    }

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