【构造题】
【构造题】
构造题,一生之敌。

构造题,有时候需要一些小巧思。
多练!!!
有时候,构造题不需要考虑太多,从最简单的地方入手即可
WA一定不要怀疑代码的问题,一定是思路问题->换思路/找之前的思路哪里有漏洞
多去尝试不同的想法,多从不同的角度看
Adrenaline Rush
https://codeforces.com/contest/2052/problem/A
思路
考虑按照赛车完赛位置的相反顺序
让每辆赛车一次一辆地超过它前面的所有赛车,然后让所有完赛更早的赛车再超过它
代码
/*【策略】
考虑按照赛车完赛位置的相反顺序
让每辆赛车一次一辆地超过它前面的所有赛车,然后让所有完赛更早的赛车再超过它。
*/
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=1010;
int n,c[N];
int a[N];
int b[N];//记录当前所有下标数的位置
//榜单从后往前看,先超过他能超过的 然后让他到达预定的位置
void swap_(int &x,int &y){
//先交换下标
int tp=b[y];
b[y]=b[x];
b[x]=tp;
//再交换数值
swap(x,y);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>c[i];
}
for(int i=1;i<=n;i++){
a[i]=i;
b[i]=i;
}
vector<PII> ans;
for(int i=n;i>=1;i--){
int t=c[i];
//先超过能超过的
for(int j=b[t];j>1;j--){
if(a[j]>a[j-1]){
ans.push_back({a[j],a[j-1]});
swap_(a[j],a[j-1]);
}
}
//再回到对应的位置
for(int j=b[t];j<i;j++){
ans.push_back({a[j+1],a[j]});
swap_(a[j],a[j+1]);
}
}
int l=ans.size();
cout<<l<<endl;
for(int i=0;i<l;i++) cout<<ans[i].first<<" "<<ans[i].second<<endl;
return 0;
}
MEX Cycle
https://codeforces.com/contest/2049/problem/C
※涉及圆圈/循环的构造题:
(1)起始坐标一般为0
(2)一般用mod解决
(3)有限制:打表找规律(各种量的奇数/偶数)
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=200010;
int t;
int n,x,y;
int a[N];
/*【构造策略】奇偶性打表
最简单的策略就是01交替->设置a[x]=0 然后绕一圈设0 1
(1)如果n是奇数:会冲突一个0或1 a[x]等于2 有相邻和朋友帮忙维持mex=2
(2)n是偶数且x-y为偶数:两个0或者两个1会冲突:改一个为2
*/
int cycle(int x){
if(x==0) return n;
if(x==n+1) return 1;
return x;
}
void solve(){
cin>>n>>x>>y;
memset(a,-1,sizeof a);
x--;y--;
/*转化为数学工具:
圆圈:mod
起始坐标最好是0
*/
for(int i=0;i<n;i++){
a[(i+x)%n]=i%2;
}
if(n%2 || abs(x-y)%2==0) a[x]=2;
//注意这里也从0到n-1
for(int i=0;i<n;i++) cout<<a[i]<<" ";
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
solve();
}
return 0;
}
Palindromic Subsequences
https://codeforces.com/contest/2056/problem/C
回文数性质


构造思路
考虑f(a)=3 形如1 2 1的序列
->前后都有1 2->凑1一个 2一个

对于1 1 n 1 -> 2~n-2 -> n-3个
对于2 2 n 2 -> 3~n-2、1 -> n-3个
->总个数2n-6个
->满足n>6的情况
->n=6时单独构造
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t,n;
void solve(){
cin>>n;
if(n==6) cout<<"1 1 2 3 1 2"<<'\n';
else{
for(int i=1;i<=n-2;i++) cout<<i<<" ";
cout<<"1 2"<<"\n";
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
solve();
}
return 0;
}
小红的“质数”寻找
https://ac.nowcoder.com/acm/contest/100902/D
质数当然是越简单越好!
代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
/*
关注首位即可 质数要是<10的就很好构造
->构造(__)0000000...
*/
//注意小细节:该题不存在无法构造的数!
int t;
string s;
void solve(){
cin>>s;
int to=s[0]-'0';
int l=s.size();
string ans;
//直接枚举首位所有情况考虑就行
if(to==1){
ans=ans+'2';
for(int i=1;i<=l-1;i++) ans=ans+'0';
}
else if(to==2){
ans=ans+'3';
for(int i=1;i<=l-1;i++) ans=ans+'0';
}
else if(to==3){
ans=ans+'5';
for(int i=1;i<=l-1;i++) ans=ans+'0';
}
else if(to==4){
ans=ans+'5';
for(int i=1;i<=l-1;i++) ans=ans+'0';
}
else if(to==5){
ans=ans+'7';
for(int i=1;i<=l-1;i++) ans=ans+'0';
}
else if(to==6){
ans=ans+'7';
for(int i=1;i<=l-1;i++) ans=ans+'0';
}
else if(to==7){
ans=ans+"11";
for(int i=1;i<=l-1;i++) ans=ans+'0';
}
else if(to==8){
ans=ans+"11";
for(int i=1;i<=l-1;i++) ans=ans+'0';
}
else if(to==9){
ans=ans+"11";
for(int i=1;i<=l-1;i++) ans=ans+'0';
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--) solve();
return 0;
}
小L的构造
https://ac.nowcoder.com/acm/contest/95337/L
本题需要对上脑电波。
思路
首先需要打表->多打一点找规律(打到n=20左右?)

->讨论出情况
n<=3时无解
->考虑n%6
(1)若n%6==0 -> 刚好六元组
(2)若n%6==1 或 n%6==2 -> 多出来的数无法造出三元组
(3)若n%6==3 -> 是可以构造出九元组的!->最后9个数借过来->剩下的一定是六元组
(4)若n%6==4 或 n%6==5 ->多出来的数可以构造出三元组
->结论
【六元组】(6n+1,6n+2,6n+4) (6n+3,6n+5,6n+6)
【九元组】(6n+1,6n+2,6n+4) (6n+3,6n+5,6n+9) (6n+6,6n+7,6n+8)
代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
int t;
int n;
void solve(){
cin>>n;
vector<array<int,3>> ans;
bool is_ok=true;
if(n<=3){
is_ok=false;
cout<<0<<endl;
}
else if(n%6==0){//构造六元组
for(int i=0;6*i+6<=n;i++){
ans.push_back({6*i+1,6*i+2,6*i+4});
ans.push_back({6*i+3,6*i+5,6*i+6});
}
}
else if(n%6==1 || n%6==2){
for(int i=0;6*i+6<=n;i++){
ans.push_back({6*i+1,6*i+2,6*i+4});
ans.push_back({6*i+3,6*i+5,6*i+6});
}
}
else if(n%6==3){
//先构造六元组
int i=0;
for(i=0;6*i+6<=n-9;i++){
ans.push_back({6*i+1,6*i+2,6*i+4});
ans.push_back({6*i+3,6*i+5,6*i+6});
}
//构造九元组
ans.push_back({6*i+1,6*i+2,6*i+4});
ans.push_back({6*i+3,6*i+5,6*i+9});
ans.push_back({6*i+6,6*i+7,6*i+8});
}
else if(n%6==4 || n%6==5){
int i=0;
for(i=0;6*i+6<=n;i++){
ans.push_back({6*i+1,6*i+2,6*i+4});
ans.push_back({6*i+3,6*i+5,6*i+6});
}
ans.push_back({6*i+1,6*i+2,6*i+4});
}
if(is_ok){
int l=ans.size();
cout<<l<<endl;
for(auto q:ans){
cout<<q[0]<<" "<<q[1]<<" "<<q[2]<<endl;
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--) solve();
return 0;
}
Creating Keys for StORages Has Become My Main Skill
MEX有关的数组构造题:从0开始往前遍历即可
https://codeforces.com/contest/2072/problem/C
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
int t;
int n,x;
void solve(){
cin>>n>>x;
//涉及mex的题目:考虑贪心 那就是从0开始遍历
int v=0;//如果位数和x不符 就直接停下即可
vector<int> ans(n,x);//为满足条件:全部设成x即可
bool is_ok=true;
for(int i=0;i<min(n-1,x);i++){
int tmp=v|i;//直接计算或即可
if((tmp&x)!=(tmp|i)){
is_ok=false;
break;
}
else{
ans[i]=i;
v=tmp;
}
}
//为增大mex -> 最后一位单独判断:如果可以等于x就有增大mex的可能
if(is_ok && (v|n-1)==x){
ans[n-1]=n-1;
}
for(auto temp:ans) cout<<temp<<" ";
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--) solve();
return 0;
}
Serval and The Formula
https://codeforces.com/contest/2085/problem/C
与位运算有关的构造题
转化式子->往简单的构造方法想
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
int t;
ll x,y;
/*
观察到性质:(x+k)+(y+k)=(x+k)⊕(y+k) -> (x+k)&(y+k)=0
那么我们可以考虑给最大的数加到更大->在一个新的幂次位变1 其他位变0
小的数到不了 只能在最高幂次内打转->无论如何都是0
->那么k=2^n-max(x,y)
*/
ll b[100]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,2147483648,4294967296,8589934592,17179869184,34359738368,68719476736,137438953472,274877906944,549755813888,1099511627776,2199023255552,4398046511104,8796093022208,17592186044416,35184372088832,70368744177664,140737488355328,281474976710656,562949953421312,1125899906842624,2251799813685248,4503599627370496,9007199254740992,18014398509481984,36028797018963968,72057594037927936,144115188075855872,288230376151711744,576460752303423488,1152921504606846976};
void solve(){
cin>>x>>y;
ll ma=max_(x,y);
if(x==y){
cout<<"-1"<<endl;
return;
}
int l=0,r=61;
while(l<r){
int mid=(l+r)>>1;
if(b[mid]>=ma) r=mid;
else l=mid+1;
}
cout<<(b[l]-ma)<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--) solve();
return 0;
}
Simple Permutation
https://codeforces.com/contest/2090/problem/D
不要看题目让你构造多少个->直接去找最优构造方法
一定要手玩一下->发现只要后面数不差太多 平均出来的结果就是第一个数
->第一个数放素数 然后让他尽可能多出现就好啦
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=2e5+10;
int t;
int n;
int primes[N],cnt;
bool st[N];
void get_primes(int x){
for(int i=2;i<=x;i++){
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=x/i;j++){
st[primes[j]*i]=1;
if(i%primes[j]==0) break;
}
}
}
void solve(){
cin>>n;
//找到离n/2最近的素数 然后构造x x-1 x+1 .. 其他随意
int l=0,r=cnt;
while(l<r){
int mid=(l+r+1)>>1;
if(primes[mid]<=n/2) l=mid;
else r=mid-1;
}
int res=primes[l];
cout<<res<<" ";
vector<bool> st(n+1,0);
st[res]=1;
for(int i=1;(res-i)>0&&(res+i)<=n;i++){
cout<<res-i<<" "<<res+i<<" ";
st[res-i]=1;
st[res+i]=1;
}
for(int i=1;i<=n;i++){
if(!st[i]) cout<<i<<" ";
}
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
get_primes(100010);
cin>>t;
while(t--) solve();
return 0;
}
小红开灯(四)
https://ac.nowcoder.com/acm/contest/107000/E
完全不需要考虑两盏灯之间的关系:有一盏灯是满足条件的即可->但要保证另一盏灯是能够在之后被遍历到的(能改)
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=110;
/*
【结论】改变相邻两盏灯的状态->不会改变奇偶性->所以一定要偶数盏灯是暗的->直接特判奇数盏灯
※不需要管两盏灯分别的状态,我只需要保证【之后不会再被遍历到的灯】变亮即可->平扫一遍
*/
int n,m;
string s[N];
int cnt=0;
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++){
string tmp;
cin>>tmp;
tmp=' '+tmp;
s[i]=tmp;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i][j]=='0') cnt++;
}
}
if(cnt%2){
cout<<"-1"<<endl;
return;
}
vector<array<int,4>> ans;
for(int i=1;i<=n;i++){
for(int j=1;j<m;j++){
if(s[i][j]=='0'){
if(j<m){
s[i][j]='1';
if(s[i][j+1]=='0') s[i][j+1]='1';
else s[i][j+1]='0';
ans.push_back({i,j,i,j+1});
}
}
}
}
for(int i=1;i<n;i++){
if(s[i][m]=='0'){
s[i][m]='1';
if(s[i+1][m]=='0') s[i+1][m]='1';
else s[i+1][m]='0';
ans.push_back({i,m,i+1,m});
}
}
cout<<ans.size()<<endl;
for(auto [aa,bb,cc,dd]:ans){
cout<<aa<<" "<<bb<<" "<<cc<<" "<<dd<<endl;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T=1;
//cin>>T;
while(T--) solve();
return 0;
}
Trulimero Trulicina
https://codeforces.com/contest/2094/problem/F
注意探讨m和k的性质->会发现与n无关
最简单的方法就是自然顺序排下去->什么情况不可以呢?
/*
※【考虑自然阅读顺序】(没考虑到这个!)
1234
5612
3456
【什么情况会失败?】
m是k的倍数
123123
123123->失败
->排列即可
123123
312312
*/
int n,m,k;
void solve(){
cin>>n>>m>>k;
if((m%k)==0){
//排列
int cnt=k;
int pos=cnt+1;
for(int i=1;i<=n;i++){
if(pos==1) pos=cnt+1;
vector<int> tmp;
//cout<<pos<<endl;
for(int j=pos;j<=cnt;j++) tmp.push_back(j);
for(int j=1;j<=pos-1;j++) tmp.push_back(j);
for(int j=0;j<m;j++){
int p=j%cnt;
cout<<tmp[p]<<" ";
}
pos--;
cout<<endl;
}
return;
}
//自然阅读顺序
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cout<<cnt+1<<" ";
cnt=(cnt+1)%k;
}
cout<<endl;
}
}
小苯的序列染色
https://ac.nowcoder.com/acm/contest/110696/D
方便思考:多点修改的问题--转化为-->单点修改问题
/*
【贪心构造题】
造数据观察性质!
要便于思考->必须总结出单点修改的规律
(1)观察到000011111111111...11100
中间的1都可以变成0,除了第二个位置不能为0
->去前导0
(2)最后一个位置一定不能是1
*/
int n;
string s;
void solve(){
cin>>n;
cin>>s;
if(s[n-1]=='1'){
cout<<"NO"<<endl;
return;
}
string tmp;
int flag=0;
for(int i=0;i<n;i++){
if(s[i]!='0')flag=1;
if(flag)tmp=tmp+s[i];
}
if(tmp.empty()){
cout<<"YES"<<endl;
return;
}
if(tmp.size()==1){
cout<<"YES"<<endl;
return;
}
if(tmp[1]=='0') cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
Racing
https://codeforces.com/contest/2110/problem/C
/*【贪心思路】
不满足条件的时候,我有什么办法能够满足条件?->先不填-1 看情况填
-1先存着:如果遇到l[i]>h的就给前面进行替换
先替换近的(从后往前)
->满足条件后 因为是一个非递减序列->贪心策略要优先填1
那么对于某个点,如果当前h+pos.size()>r[i]->要提前填0
*/
int n;
void solve(){
cin>>n;
vector<int> d(n+1,0);
for(int i=1;i<=n;i++) cin>>d[i];
vector<int> l(n+1,0),r(n+1,0);
for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
vector<int> pos;
int h=0;
for(int i=1;i<=n;i++){
if(d[i]==-1){
pos.push_back(i);
}
else{
h+=d[i];
}
while(h<l[i]){
if(pos.empty()){
cout<<"-1"<<endl;
return;
}
int t=pos.back();
d[t]=1;
pos.pop_back();
h++;
}
while(h+pos.size()>r[i]){
if(pos.empty()){
cout<<"-1"<<endl;
return;
}
int t=pos.back();
d[t]=0;
pos.pop_back();
}
}
while(pos.size()){
int t=pos.back();
d[t]=0;
pos.pop_back();
}
for(int i=1;i<=n;i++){
cout<<d[i]<<" ";
}
cout<<endl;
}
Mex in the Grid
https://codeforces.com/contest/2102/problem/C
题目大意
构造所有子网格的mex之和最大化
要让0 1 2 3 ...尽可能靠在一起->螺旋线
代码
const int N=1010;
int n;
int ans[N][N];
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
/*
构造螺旋线即可->步长/方向
*/
void solve(){
cin>>n;
if(n==1){
cout<<"0"<<endl;
return;
}
memset(ans,0,sizeof ans);
int x,y;
if(n%2==0){
x=n/2-1;y=n/2-1;
}
else{
x=n/2;y=n/2;
}
ans[x][y]=0;
int cnt=1,step=1;//计数 步长
while(cnt<=n*n-1){
for(int i=0;i<4;i++){
int steps=step;
step+=i%2;//左右方向递增步长
for(int j=1;j<=steps;j++){
x+=dx[i];y+=dy[i];
if(x<n && x>=0 && y<n && y>=0){
ans[x][y]=cnt++;
}
if(cnt>=n*n) break;
}
if(cnt>=n*n) break;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<ans[i][j]<<" ";
}
cout<<endl;
}
}
Make it Zero
https://codeforces.com/contest/2124/problem/E
题目大意
构造若干个数组 使得这些数组每一位上的数之和等于原先数组对应每一位
构造的数组满足一个地方前后缀和相同
思路
※减去的数要满足前缀和=后缀和:偶数才能正好减完
1.先考虑什么情况下会-1:
(1)总和sum为奇数:减到最后会剩下单独的1 凑不出前缀和
(2)有一个数>sum/2:这个数没办法凑前缀和来减掉
2.再考虑怎么减:贪心的想最小的方案
(1)如果可以构造sum/2的前后缀:直接减掉即可
(2)否则需要构造两次:
①减掉最靠近sum/2的前后缀
设x=sum/2 则前后可平分x*2
②剩下的数
设extra=sum-x*2
中点后第一个数(a[idx+1])直接为extra/2
后面的数依次分即可
*2*x和extra都一定是偶数
注意点:我如何保证a[idx+1]>=extra/2?
因为a[1~idx]加上a[idx+1]后一定>sum/2
那么有a[idx+1]>sum/2-a[1~idx]
extra=sum-a[1~idx]*2
即extra/2==sum/2-a[1~idx]
代码
int n;
void solve(){
cin>>n;
vector<i64> a(n+1,0);
vector<i64> s(n+1,0);
i64 sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
s[i]=s[i-1]+a[i];
}
if(sum%2){
cout<<-1<<endl;
return;
}
for(int i=1;i<=n;i++){
if(a[i]>sum/2){
cout<<-1<<endl;
return;
}
}
int idx=0;
for(int i=1;i<n;i++){
if(s[i+1]>sum/2){
idx=i;
break;
}
}
if(s[idx]==sum/2){
cout<<1<<endl;
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
cout<<endl;
}
else{
vector<i64> b(n+1,0);
i64 extra=sum-2*s[idx];
b[idx+1]=extra/2;
a[idx+1]-=b[idx+1];
extra/=2;
for(int i=n;i>idx+1&&extra;i--){//凑前缀和
if(a[i]>=extra){
a[i]-=extra;
b[i]=extra;
extra=0;
}
else{
b[i]=a[i];
extra-=a[i];
a[i]=0;
}
}
cout<<2<<endl;
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
for(int i=1;i<=n;i++){
cout<<b[i]<<" ";
}
cout<<endl;
}
}
Least Unbalanced
https://atcoder.jp/contests/abc422/tasks/abc422_d
构造二进制长度数组
题目大意


思路
答案只会是0和1
数组生成:\(N<=20\) 逆着题目意思直接模拟即可 不用推数学公式!!!
每次平均拆分
代码
int n,k;
void solve(){
cin>>n>>k;
vector<int> ans={k};
for(int i=1;i<=n;i++){
vector<int> nxt;
for(auto son:ans){
nxt.push_back(son/2);
nxt.push_back(son-son/2);
}
ans=nxt;
}
int mx=0,mi=1e9;
for(auto son:ans){
mx=max(mx,son);
mi=min(mi,son);
}
int res=mx-mi;
cout<<res<<endl;
for(auto son:ans) cout<<son<<" ";
cout<<endl;
}
试炼·终境
https://ac.nowcoder.com/acm/contest/117057/D
题目大意

思路
显然要让二进制位互补
先计算\(\sum i \oplus a_i\)可能的上界:
对于每一个二进制位,若1-n的所有数位上1的和>0的和->肯定要浪费至少一个1
->上界为\(\sum i\)减去所有(1的和>0的和)的二进制位的值
->构造方法:

为什么这样构造是可行的?
打表可得:0 1 0 3 2 1 0 7 6 5 4 3 2 1 0 ...
确定n'为n的取反,构造[n', n]区间,区间内反向即可以达到互补的效果
往下分治讨论即可
代码
int n;
//取反
int cal(int x){
if(x==0) return 0;
int m=1;
while(m<=x/2){
m<<=1;
}
int res=(m<<1)-1;
res^=x;
return res;
}
void solve(){
cin>>n;
vector<int> ans(n+1,0);
int n1=n;
while(n1>0){
int cn=cal(n1);
if(cn!=0){
for(int i=cn;i<=n1;i++){
ans[i]=cn-i+n1;
}
n1=cn-1;
}
else{
ans[n1]=n1;
n1--;
}
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}

浙公网安备 33010602011771号