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)的奇偶性就行。