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;
}
posted @ 2025-04-08 13:02  肆惠  阅读(82)  评论(0)    收藏  举报