codeforce刷题(六)

1、Necklace Assembly

给一个长度为(n)的字符串,从中挑选出一些字符然后按照你想要的顺序排成一个环,这个环顺时针旋转(k)次后与原来的环相同,问环的最大长度。

(1<=n,k<=2000)

题解

旋转(k)次后依然相同,说明这个环的循环节是(k)的因数。

由于(n,k) 比较小,可以枚举循环节的长度和环的最大长度,复杂度(O(n^2))

int main()
{
    int n, k, t;
    int cnt[40], tot[2005];
    string s;

    cin >> t;
    while(t--) {
        memset(cnt, 0, sizeof(cnt));

        cin >> n >> k >> s;

        _myfor(i, 0, n) cnt[s[i] - 'a']++;

        int ans = 0;
        myfor(i, 1, min(n, k)) if (k % i == 0) {   // 枚举循环节的长度
            for (int le = n; le > 0; le--) if (le % i == 0) {      // 枚举环的最大长度
                int t = le / i, tot = 0;
                _myfor(j, 0, 26) if (cnt[j] >= t) tot += (cnt[j] / t);
                if (tot >= i) {
                    ans = max(ans, le);
                    break;
                }
            }
        }
        
        cout << ans << endl;
    }
    return 0;
}

2、Task On The Board

给一个字符串(s),长度为(m)的整数序列(b),请构造出满足如下条件的字符串(str)

  • (b[i] = sum_{str[i] < str[j]}|i-j|)

(1 <= m <= len(s) <= 50)

题解

显然,如果(b[i] = 0) 说明第 (i) 位置的字符在字符串(str)中的字典序最大。

所以我们可以不断找出(b[i])(0)的位置,然后依次填入相应的字符。

根据已知的(b[i] = 0)的位置(原始序列(b)),不断减去这些位置上字符的贡献:(b[j] = b[j] - sum_{str[j] < str[i]}|i - j|),就能得到下一轮哪些位置((b[some\_{positon}] = 0))可以填。

倒推

sort(s.begin(), s.end());

int n = s.size();

vector<int> id;
while(true) {
    id.clear();
    // 找b[i] == 0的位置
    myfor(i, 1, m) if (!b[i] && !use[i]) {
    	id.push_back(i);
    	use[i] = 1;
	} 
	if ((int)id.size() <= 0) break;
	// 填入字符
	char tp;
	for (int i = n - 1; i >= 0; i--) {
		if (cnt[s[i] - 'a'] >= (int)id.size()) {
			tp = s[i];
			cnt[tp - 'a'] = 0;
			break;
		}
		else cnt[s[i] - 'a'] = 0;
	}
	// 减去上一轮那些位置的贡献
	for (auto i: id) {
		ans[i] = tp;
		myfor(j, 1, m) if (b[j]) b[j] -= abs(j - i);
	}
}
myfor(i, 1, m) cout << ans[i];
cout << endl;

3、Two Divisors

(n)个数,问每个数((a_i))是否存在两个因数(d_1)(d_2),使得(gcd(d_1 + d_2,a_i) = 1)

(1<=n<=5*10^5,2<=a_i<=1*10^7)

题解

已知,任意一个数都可以写成几个质因数的乘积,比如(8 = 2^3,6 = 2 * 3​。)

那么将这些质因数分成两个不相交的集合(s_1)(s_2),令(d_1= s1)中所有数的乘积,(d_2)同理,

(gcd(d_1 + d_2, a_i) = 1) ,所以该问题等价于求(a_i)的两个不同的质因数。

最好用线性筛,在筛的同时求出每个合数的最小质因数。

/*
time:1800ms
*/
void Inite() {
    prime[1] = 1;
    for (int i = 2; i * i <= 10000009; i++) if (!prime[i]) {
        p.push_back(i);
        for (int j = i + i; j < N; j += i) prime[j] = 1;
    }
}

int main()
{
    Inite();
    
    cin >> n;
    myfor(i, 1, n) cin >> a[i];

    vector<pair<int, int> > ans;

    myfor(i, 1, n) {
        int tp = a[i];
        bool flag = false;
        for (auto x: p) if (a[i] % x == 0) {
            while(a[i] % x == 0) a[i] /= x;
            if (a[i] != 1) ans.push_back(make_pair(tp / a[i], a[i]));
            else ans.push_back(make_pair(-1, -1));
            flag = true;
            break;
        }
        if (!flag) ans.push_back(make_pair(-1, -1));
    }

    for (auto &x: ans) cout << x.first << " ";
    cout << endl;
    for (auto &x: ans) cout << x.second << " ";
    cout << endl;

    return 0;
}

4、Ehab and Prefix MEXs(好题)

给一个长度为(n)的序列(a),求满足如下条件且长度为(n)的序列(b):

  • 对于任意一个(i(1<=i<=n))(MEX({b_1,b_2,···,b_i}) = a_i)

(MEX({···}))是指:不在给定集合的最小非负数,比如(MEX({1,2,3}) = 0)(MEX{0,1,3} = 2)

(a_i <= a_{i + 1})(0<=a_i<=i)

题解

构造方法:假设(a)中有(x)个不同的数(去重后统计)。如果(a[i] != a[i - 1]),那么(b[i] = a[i - 1]),因为(b[1],b[2],···,b[i - 1])都取不到(a[i -1])。这就填了(x - 1)个位置,然后剩下的(n - x + 1)个位置从小到大依次填入(a)中没有出现的数((n + 1 - x)个),刚好填完。

困难在于怎么处理(a[i] ==a[i -1])的情况

//code_source: https://blog.csdn.net/qq_45458915/article/details/106822285

5、Game On Leaves

张三和李四在玩游戏:给一颗有(n)个节点的无根树,和特定的节点(x)。每轮的玩家选定一个(degree <= 1)的节点并将其移除,同时删除以该节点为端点的所有边。谁先移除节点(x),则其成为赢家。在游戏过程中,他们都采取了最优的策略。问最后谁赢?(张三先手)

题解

Select any leaf node in the tree and remove it together with any edge which has this node as one of its endpoints. A leaf node is a node with degree less than or equal to 1.

无根树的叶子节点的度怎么等于0? 估计是(n)可能为1吧,费解

两个人的最优策略是:优先删除其他无关的节点,尽量将(x)留在最后。又因为每次只删除叶子节点,所以判断一下(n - 1)的奇偶性就行。

原文地址:https://www.cnblogs.com/zgglj-com/p/13266362.html