[ABC347D] Popcount and XOR题解
提供一种好想且与题解截然不同的做法
首先看到异或想到了 01trie,拆位。这是 abc 的 d,且还有两个条件,01trie 貌似有点大材小用了,所以去想拆位。我们可以按位考虑来满足异或的条件。考虑怎么满足 popcount 的条件。想到 dp,设计 \(dp[i][j][k]\) 为当前做到 \(i\) 位,\(x\) 用了 \(j\) 个 \(1\),\(y\) 用了 \(k\) 个 \(1\) 是否可行,然后转移方程易得。
\(dp[i][j][k]=dp[i-1][j-1][k-1]|dp[i-1][j][k]\)
\(dp[i][j][k]=dp[i-1][j][k-1]|dp[i-1][j-1][k]\)
第一个转移方程的意思是如果 \(c\) 这位是 \(0\),那么 \(x\),\(y\) 这位要么都是 \(0\),要么都是\(1\),所以要么都多用 \(1\) 个,要么都不用。如果 \(c\) 这位是 \(1\),那么 \(x\),\(y\) 这位只有一个这位是 \(1\),所以要么 \(x\) 多用 \(1\) 个,要么 \(y\) 多用 \(1\) 个。然后题目让我们输出构造出来的数,我们只要记录一下路径就好了
#include<bits/stdc++.h>
#define for1(i,a,b) for(ll i=(a);i<=(b);i++)
#define for2(i,a,b) for(ll i=(a);i>=(b);i--)
#define mx(a,b) max(a,b)
#define mn(a,b) min(a,b)
#define puba push_back
#define mem(a) memset((a),0,sizeof((a)))
#define ll long long
using namespace std;
struct node{
ll x,y,z;
}path[65][65][65];
ll a,b,c,dp[65][65][65];
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>a>>b>>c;
dp[0][0][0]=1;
for1(i,1,60){
for1(j,0,mn(a,i)){
for1(k,0,mn(b,i)){
ll x=(c>>(i-1ll))&1ll;
if(!x){
if(dp[i-1][j][k]){
path[i][j][k]={i-1,j,k};
dp[i][j][k]=dp[i-1][j][k];
}if(j>=1&&k>=1&&dp[i-1][j-1][k-1]){
dp[i][j][k]=dp[i-1][j-1][k-1];
path[i][j][k]={i-1,j-1,k-1};
}
}else{
if(j>=1&&dp[i-1][j-1][k]){
dp[i][j][k]=dp[i-1][j-1][k];
path[i][j][k]={i-1,j-1,k};
}if(k>=1&&dp[i-1][j][k-1]){
path[i][j][k]={i-1,j,k-1};
dp[i][j][k]=dp[i-1][j][k-1];
}
}
}
}
}
if(dp[60][a][b]){
ll x=60,y=a,z=b,ans1=0,ans2=0;
while(x){
if(path[x][y][z].y+1==y) ans1+=(1ll<<(x-1));
if(path[x][y][z].z+1==z) ans2+=(1ll<<(x-1));//要加1ll,代表这个1是long long否则会炸
int nx=path[x][y][z].x;
int ny=path[x][y][z].y;
int nz=path[x][y][z].z;//注意这里要临时存一下,不能现改,否则会影响下面
x=nx,y=ny,z=nz;
}
cout<<ans1<<" "<<ans2<<"\n";
}else cout<<"-1";
return 0;
}