题意:
给定一个长度为\(n\)的序列,现在要求你对其进行染色,\(r\ or\ b\),两个颜色,要求染完色的序列,在其中选择尽量多对的下标\(1\le i\le j\le n\),使得序列\([i,j]\)中的\(r\)个数为奇数。
如果最多的答案对数对应了多种染色方案,如果不超过\(100\)个,请全部输出,否则输出前\(100\)个。
思路:
考虑另一个序列\(p_i\)表示前缀\([1,i]\)中,含有的\(r\)的个数,那么要是使得区间\([i,j]\)的\(r\)的个数为奇数,即\(p_{i-1},p_j\)的奇偶性应该不同。
那么假如我们设\(p\)序列中,前缀中\(r\)为奇数的位置的数量为\(x\),前缀中\(r\)为偶数的位置的数量为\(y\),那么我们最多的对数就可以表示为\(x*y\),而且我们应当\(p_0\)这个值考虑到,即共\(n+1\)个位置,那么考虑什么时候能够使得\(x+y=n+1\),且\(x*y\)取到最大值,通过均值的不等式,就知道令\(x = (n + 1) / 2,y = ((n + 1) + 1) / 2\),或者二者相反即是答案。
我们令\(p_i = 0\),代表是偶数,\(p_i = 1\),代表是奇数。
爆搜枚举当前位置,是偶数还是奇数,通过\(x*y\)取得最大值的时候的个数的数量关系,剪枝掉个数大于一半的情况,考虑字典序从小到大输出,那么肯定是能先放\(b\)就放\(b\),而\(b\)是不会改变\(p\)序列的奇偶性的,所以要想先多放\(b\),那就优先考虑令当前位置和前一个位置的奇偶性相同,否则才考虑放置\(r\),所以我们只需要爆搜出一个代表奇偶的\(01\)序列,然后往对应位置上填\(b,r\)即可。
注意如果是爆搜枚举放置\(b,r\)那就错了,因为我们考虑的奇偶性,所以我们枚举的奇偶数量。
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e5 + 10;
int is_odd[N],n;
int cnt;
void dfs(int cnt0,int cnt1,int pos) {
if(cnt1 > (n + 2) / 2 || cnt0 > (n + 2) / 2) return ;//可行性剪枝 大于这个数根本不可行
if(pos == n + 1) {
for(int i = 1;i <= n;i ++) {
if(is_odd[i] == is_odd[i-1]) printf("b");
else printf("r");
}
cnt++;
printf("\n");
}
is_odd[pos] = is_odd[pos-1];
if(is_odd[pos-1] == 0) {
dfs(cnt0+1,cnt1,pos+1);
if(cnt == 100) return ;
}
else {
dfs(cnt0,cnt1+1,pos+1);
if(cnt == 100) return ;
}
is_odd[pos] = is_odd[pos-1]^1;
if(is_odd[pos-1] == 0) {
dfs(cnt0,cnt1+1,pos+1);
if(cnt == 100) return ;
}
else {
dfs(cnt0+1,cnt1,pos+1);
if(cnt == 100) return ;
}
}
int main() {
scanf("%d",&n);
//把0位置也要算进来 因为是前缀的数量
ll ans = 1ll * ((n + 1) / 2) * ((n + 2) / 2);
// cout << ans << '\n';
printf("%lld\n",ans);
dfs(1,0,1);
return 0;
}