[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;
}
posted @ 2024-03-31 20:09  wuhupai  阅读(46)  评论(0编辑  收藏  举报