【构造题】

【构造题】

构造题,一生之敌。

image

构造题,有时候需要一些小巧思。
多练!!!
有时候,构造题不需要考虑太多,从最简单的地方入手即可
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

回文数性质

image
image

构造思路

考虑f(a)=3 形如1 2 1的序列
->前后都有1 2->凑1一个 2一个
image
对于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左右?)
image
->讨论出情况

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
构造二进制长度数组

题目大意

7f478b66-e98f-4f4a-b079-df59742a9f5c
7448e4db-e5f8-42ba-8900-0d9f8bb799a1

思路

答案只会是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

题目大意

QQ_1758340612731

思路

显然要让二进制位互补

先计算\(\sum i \oplus a_i\)可能的上界
对于每一个二进制位,若1-n的所有数位上1的和>0的和->肯定要浪费至少一个1
->上界为\(\sum i\)减去所有(1的和>0的和)的二进制位的值

->构造方法:
QQ_1758340890947
为什么这样构造是可行的?
打表可得: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;
}
posted @ 2025-01-23 20:28  White_ink  阅读(34)  评论(0)    收藏  举报