Codeforces Round 1014 (Div. 2)(C-F)题解
C 结论
题意:给你一个序列a,任取其中不同的两个正数,和为偶数,将其中一个减少1,另一个增加1,问最后最大值
思路:
全奇或者全偶直接输出最大值
其余情况,显然剩下只要存在奇数,那么某一个奇数可以将偶数全部吸收完,或者偶数吸收奇数
设偶数X个,那么一个奇数吸收X-1个偶数,剩下来的那个偶数将其余所有奇数吸收到只剩1,最后可以再吸收奇数的一个1,那么答案就是序列和减去奇数个数加1
D 思维
题意:给你长为n的由L,I,S组成的字符串,每次可以在xy之间插入z,构造方案使得三种字符个数相等
考虑xy,xy->xzy->xyzy,发现可以用2次操作,使得最大值相对-1,因而2n必定是可以构造的,除非只有一种字符,代码不想敲了,贴个哥哥的
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
if (std::count(s.begin(), s.end(), s[0]) == n) {
std::cout << -1 << "\n";
return;
}
std::vector<int> a(n);
int cnt[3] {};
for (int i = 0; i < n; i++) {
a[i] = s[i] == 'L' ? 0 : s[i] == 'I' ? 1 : 2;
cnt[a[i]]++;
}
std::vector<int> ans;
int min = *std::min_element(cnt, cnt + 3);
for (int x = 0; x < 3; x++) {
if (cnt[x] == min) {
continue;
}
int p = 1;
while ((a[p] == x) == (a[p - 1] == x)) {
p++;
}
while (cnt[x] > min) {
if (a[p] == x) {
int y = a[p - 1];
a.insert(a.begin() + p, 3 - x - y);
ans.push_back(p);
a.insert(a.begin() + p + 1, y);
ans.push_back(p + 1);
p += 2;
} else {
int y = a[p];
a.insert(a.begin() + p, 3 - x - y);
ans.push_back(p);
a.insert(a.begin() + p, y);
ans.push_back(p);
}
cnt[x]--;
}
}
std::cout << ans.size() << "\n";
for (auto x : ans) {
std::cout << x << "\n";
}
}
E 结论+分析
题意:给你个01网格图,其中一些点已经染色,问使得相邻(4个方向)颜色(每对算1次)不同数量为偶的染色方案数
思路:
形式化一下题意:考虑点i贡献,点i邻居数为\(deg_i\)
那么$\sum col_i \oplus col_j≡\sum(col_i + col_j)≡∑col_j+deg_i*col_i\ mod\ 2 $
由此全体贡献为\(\sum col_i*deg_i\)
只用考虑邻居数为奇的点,因为题目给定边长≥3,所以只会出现在边界非四角上
设这些点的集合为T,剩余未被染色点个数X
若T已经被染色完,若全体异或为1,那么方案为0;不然就是\(2^{X}\)
若T未被染色完,那么T中未被染完的点的颜色异或和必须和T中被染完的点异或和相同
方案数是均等的,假设T中剩余R未被染色,那么T中染色方案为\(2^{R}/2=2^{R-1}\)
总方案数\(2^{R-1}*2^{X-R}=2^{X-1}\)
F Andryusha and CCB 性质分析
题意:定义字符串的beauty为不同相邻对个数吗,例如0110,就是2
给你一个01字符串,要求将其分割成k段,beauty相同的连续子串
对其所有前缀求,满足条件的k的个数
分析:记beauty为b,将连续的0或者1合并成x,那么b+1个x可以的到一个b
如何计算答案,考虑求每个b对于序列的贡献,那么对于上面的b来说,对于第b+1个x所在区间都有贡献
所以初步是枚举b来计算
考虑到能分成的段数k的数量是合并总数sz/b,那么内循环枚举k,次数上界是NlogN
考虑固定b,k和贡献区间是否有递推性?
假设k的时候贡献区间为[l,r](l,r为合并块的标号),那么k+1的贡献区间显然可以是[l+b+1,r+b+1]
又若当前的对k有贡献最后一个合并整体的数量大于1,那么可以将这个区间同时分给k和k+1
那么k+1的贡献区间可以扩展到[l+b,r+b+1]
特别的,发现0到1并不满足上面的递推性,需要单独计算
0的时候,每个前缀i的k取值是[合并块个数,i+1](i是0-index的)
void solve(){
cin>>n;
vector<int>ans(n+1,0);
string s; cin>>s;
vector<int>b,l,r;
for(int i=0;i<n;i++){
int left=i;
while(i+1<n&&s[i+1]==s[i]){
i++;
}
int right=i;
for(int j=left;j<=right;j++) ans[j]+=j-b.size()+1;
b.push_back((right==left?1:2));
l.push_back(left);
r.push_back(right);
}
vector<int>add(b.size(),0);
for(int c=1;c<b.size();c++){
int l=c,r=c;
// int k=1;
while(l<b.size()){
add[l]++;
if(r+1<b.size()) add[r+1]--;
if(b[l]==2) l+=c;
else l+=c+1;
r+=c+1;
// k++;
}
}
int del=0;
for(int i=0;i<b.size();i++){
del+=add[i];
for(int j=l[i];j<=r[i];j++){
ans[j]+=del;
}
}
for(int i=0;i<n;i++) cout<<ans[i]<<' '; cout<<endl;
}

浙公网安备 33010602011771号