• 「题解」Codeforces 755F PolandBall and Gifts


    orz qyc

    看成一个人 (i)(p_i) 连边,每个点的入度出度都为 (1) 。那么就是若干个环,每次可以选择一条边将这条边两端的端点染色,求 (k) 次染色后,最大和最小有颜色点的个数数。

    最大值发现可以贪心,偶数长度的环可以用长度除以 (2) 次染色全部染色,每次染色的贡献都拉满了。奇数长度的环可以用长度减一除以 (2) 次染色将除了一个点其他全部染色,剩下的一个点最后还有染色机会再染。

    最小值的话,每个环第一次开始染它色一定是贡献为 (2),基于贪心先不开其他的环,再继续对这个环染色,每次贡献为 (1),到最后一步贡献为 (0)。换而言之,开了一个环就将它染色到底。

    发现对于一个长度为 (a_i) 的环,将其全部染色次数为 (a_i),贡献为 (a_i)。那么一定想让若干个 (a_i) 组成 (k),否则 (k) 多开的没染完色的那个环会多贡献 (1)

    问题转化为给定若干个正整数 (a_i),其和为 (n),问能不能挑出一些 (a_i) 使得和为 (k)。也就是 01 背包可行性问题。

    发现 bitset 和分治 NTT 都不大能过,注意到对 (a_i) 总和的限制,不同的 (a_i) 仅有 (mathcal{O}(sqrt{n})) 个,把多个同样的物品合成一个物体有不同的次数,就变成了一个多重背包问题。

    再加个二进制分组优化就冲过去了,时间复杂度不大会算反正能过

    有一个有理有据的方法是类似根号分治,选择一个值域 (T),小于等于 (T) 的数跑 (mathcal{O}(kT)) 的多重背包可行性,每次枚举物品重量 (i) 之后再记录一个 (g_j) 代表用重量 (leq i) 个物品,(j) 最少被几个 (i) 凑出来,就能 dp 了。(>T) 的用之前说到的 01 背包可行性问题,复杂度是 (mathcal{O}(frac{nk}{wT})) 的,也就是 (>T) 的数不会超过 (frac{n}{T}) 个,加上 bitset 优化即可。

    后面那个做法的复杂度为 (mathcal{O}(kT+frac{nk}{wT})),将 (T) 设为 (100) 发现计算次数可以接受,加上这个很难会被卡满,就过了。

    代码是前面没有根号分治的做法。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<bitset>
    typedef long long ll;
    template <typename T> T Max(T x, T y) { return x > y ? x : y; }
    template <typename T> T Min(T x, T y) { return x < y ? x : y; }
    template <typename T> T Abs(T x) { return x < 0 ? -x : x; }
    template <typename T> T chkmax(T &x, T y) { return x = x > y ? x : y; }
    template <typename T>
    T &read(T &r) {
    	r = 0; bool w = 0; char ch = getchar();
    	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
    	while(ch >= '0' && ch <= '9') r = (r << 3) + (r <<1) + (ch ^ 48), ch = getchar();
    	return r = w ? -r : r;
    }
    const int N = 1000010;
    int n, p[N], a[N], m, k, ans1, ans2;
    bool vis[N];
    int ct[N], b[N], c[N], us[N];
    std::bitset<1000001>f;
    void dfs(int x, int s) {
    	if(vis[x]) {
    		a[++m] = s;
    		return ;
    	}
    	vis[x] = 1;
    	dfs(p[x], s+1);
    }
    signed main() {
    	read(n); read(k); int _n = n;
    	for(int i = 1; i <= n; ++i) read(p[i]);
    	for(int i = 1; i <= n; ++i)
    		if(!vis[i])
    			dfs(i, 0);
    	n = m;
    	std::sort(a + 1, a + n + 1);
    	int k_ = k;
    	for(int i = 1; i <= n; ++i) {
    		if(!k_) break ;
    		int t = Min(a[i] / 2, k_);
    		ans2 += t * 2; k_ -= t;
    	}
    	for(int i = 1; i <= n; ++i)
    		if((a[i] & 1) && k_)
    			++ans2,
    			--k_;
    	//
    	for(int i = 1; i <= _n; ++i) ct[a[i]]++;
    	m = 0;
    	for(int i = 1; i <= _n; ++i)
    		if(ct[i])
    			b[++m] = i,
    			c[m] = ct[i];
    	f[0] = 1;
    	for(int i = 1; i <= m; ++i) {
    		int tc = c[i];
    		for(int j = 1; tc; j <<= 1) {
    			int t = Min(j, tc);
    			f |= f << (b[i] * t),
    			tc -= t;
    		}
    	}
    	if(f[k]) ans1 = k;
    	else ans1 = k+1;
    	printf("%d %d
    ", ans1, ans2);
    	return 0;
    }
    
  • 相关阅读:
    SpringCloud就是分布式啊?!?!?!
    有没有可能让运算符作为函数参数?
    【问题】用C++结构体实现顺序表,数据超过数组长度后,表长自动+1
    数学模型与数据结构的丝连
    最浅显易懂的数据库索引讲解
    whois 信息
    旁站C段查询
    网络搜索引擎
    服务指纹识别
    绕过CDN查找ip方法总结
  • 原文地址:https://www.cnblogs.com/do-while-true/p/15047873.html
Copyright © 2020-2023  润新知