交换如果相等长度和总和
3a51_
·
·
题解
::::info[Hint 1]
尝试寻找不变量。
::::
::::info[Hint 2]
尝试对于所有满足上述条件的 $a,b$ 寻找操作方案,即证明充要。
::::
::::info[Hint 3]
操作次数的限制较为宽松,尝试每步使用形如 `1000...000` 和 `000...0001` 的操作归位一个 `1`,以此构造出操作次数 $\leq n$ 的方案。
::::
::::info[Hint 4]
尝试对序列做一些变换使得操作次数 $\leq \lfloor\frac{n}{2}\rfloor$。
::::
::::info[Solution]
先阅读所有提示。
显然操作不能改变 `1` 的个数。
发现操作不会改变所有 `1` 的下标和。证明是简单的,假设交换的区间为 $[l,r]$ 和 $[l',r']$ 且满足 $r<l'$,那么交换过后 $[l',r']$ 内每个 `1` 下标减少了 $l'-l$,$[l,r]$ 内每个 `1` 下标增加了 $l'-l$,故 `1` 的下标和相等。
合理猜测上述条件充要。尝试构造方案。
手搓几次操作,观察到操作长度为 $2$ 的区间等价于将一个 `1` 向后挪一位,另一个 `1` 向前挪一位。于是不难想到一个思路,考虑每次操作归位一个 `1`,使用 `000...0001` 和 `1000...000` 进行操作,等价于一个 `1` 往后挪若干位,另一个 `1` 往前挪若干位。每次挑选一个 `1` 归位即可。
然而,上述做法操作次数最大可以达到 $n$,需要优化。观察到当 `1` 的个数超过 $\lfloor\frac{n}{2}\rfloor$ 时操作次数才有可能超出限制,而此时 `0` 的个数一定不超过 $\lfloor\frac{n}{2}\rfloor$,故只需把原序列按位翻转即可。
于是,我们对于每一个满足 `1` 下标和相等且 `1` 数量相等的 $a$ 和 $b$ 都能构造出方案,即证明了其充要。
实现时,先判掉无解,然后将两序列的 `1` 一一匹配,分为往左挪和往右挪两类,一次挪一个即可。记得输出时,把靠右的区间输出在后面。
::::
::::success[Code]
```cpp
#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define _rep(i,a,b) for(int i=(a);i>=(b);i--)
#define pi pair<int,int>
#define arr(a) array<int,(a)>
#define F fflush(stdout)
#define RE return puts("No"),void()
#define cases(_) while((_)--) solve();
using namespace std;
const int N=200005;
int _=1,n,a[N],b[N],s;
vector<arr(3)> ans;
vector<pi> pre,suf;
vector<int> va,vb;
void solve(){
scanf("%d",&n),s=0,ans.clear(),pre.clear(),suf.clear(),va.clear(),vb.clear();
rep(i,1,n){
scanf("%d",&a[i]);
if(a[i]) va.push_back(i),s+=i;
}
rep(i,1,n){
scanf("%d",&b[i]);
if(b[i]) vb.push_back(i),s-=i;
}
int m=(int)va.size();
if(m!=(int)vb.size() || s) RE;
if(m>n/2){
rep(i,1,n) a[i]^=1,b[i]^=1;
va.clear(),vb.clear();
rep(i,1,n){
if(a[i]) va.push_back(i);
if(b[i]) vb.push_back(i);
}
m=n-m;
}
m--;
rep(o,0,m){
if(va[o]<vb[o]) pre.push_back({va[o],vb[o]});
else if(va[o]>vb[o]) suf.push_back({va[o],vb[o]});
}
int l=(int)pre.size()-1,r=0;
while(l!=-1 && r!=(int)suf.size()){
int pl=pre[l].second-pre[l].first,pr=suf[r].first-suf[r].second,cur=min(pl,pr);
ans.push_back({pre[l].first,suf[r].first-cur,cur});
pre[l].first+=cur,suf[r].first-=cur;
if(pl==cur) l--;
if(pr==cur) r++;
}
puts("Yes");
printf("%d\n",(int)ans.size());
for(arr(3) t:ans){
if(t[0]+t[2]<t[1]) printf("%d %d %d %d\n",t[0],t[0]+t[2],t[1],t[1]+t[2]);
else printf("%d %d %d %d\n",t[1],t[1]+t[2],t[0],t[0]+t[2]);
}
}
signed main(){
scanf("%d",&_);
cases(_);
return 0;
}
```
::::