ABC
D - Masked Popcount

D - Masked Popcount

解説

問題 (opens in a new tab)

次のように式変形をしていく。

k=0Npopcount(k&M)=k=0N((k&M)にて立っているbitの個数)=k=0Nj=0((k&M)にてjbit目が立っていれば1,そうでなければ0)=j=0k=0N((k&M)にてjbit目が立っていれば1,そうでなければ0)\begin{aligned} \sum_{k=0}^{N}\mathrm{popcount}(k \& M) &= \sum_{k=0}^{N}((k \& M)\text{にて立っているbitの個数})\\ &= \sum_{k=0}^{N}\sum_{j=0}^\infty((k \& M)\text{にて}j\text{bit目が立っていれば}1, \text{そうでなければ}0)\\ &= \sum_{j=0}^\infty\sum_{k=0}^{N}((k \& M)\text{にて}j\text{bit目が立っていれば}1, \text{そうでなければ}0)\\ \end{aligned}

ここで、MM は定数から、次が言える。

  • もし MMjj bit 目が立っていなければ、常に 00
  • もし MMjj bit 目が立っていなければ
    • kkjj bit 目が立っているとき、11
    • kkjj bit 目が立っていないとき、00

また、制約条件から、MM6060 bit 目以降はすべて 00 であることがわかる。

したがって、 j=0,1,,59j = 0,1,\dots,59 に対して、以下を繰り返す。

  • MMjj bit 目が立っているとき、00 から NN までの kk に対して、jj bit 目が立っているものの個数を求める
  • MMjj bit 目が立っていないときはなにもしない

つまり、問題文は次のように言い換えられる。

00 以上 NN 以下の整数 kk に対して、kkjj bit 目が立っているものの個数を求めよ。

これを j=0,1,,59j=0,1,\dots,59 に対して求め、その総和を求めることで解ける。

次の事実を用いることで、上の問題を解くことができる。

kk を非負整数としたとき、 00 以上 k×2j+1k\times2^{j+1} 未満の整数のうち、 jj bit 目が立っているものの個数は、 k×2jk\times2^j

また、次も言える。

kk を非負整数、 ll2j+12^{j+1} 未満の整数としたとき、k×2j+1k\times2^{j+1} 以上 k×2j+1+lk\times2^{j+1}+l 未満の整数のうち、 jj bit 目が立っているものの個数は、 max{0,l2j+1}\mathrm{max}\{0,l-2^j+1\}

この二つを合わせて、 00 以上 NN 未満の整数のうち、jj bit 目が立っているものの個数を求めることができる。

提出コード

  • Go
const MOD = 998244353
 
func f(j int, n int) int {
	p2 := 1 << uint(j)
	k := n / (2 * p2)
	res := k * p2
	l := n % (2 * p2)
	res += max(0, l-p2+1)
	return res
}
 
func main() {
	var n, m int
	scanIntVariables(&n, &m)
	ans := 0
	for j := 0; j < 60; j++ {
		if 1 & (m>>uint(j)) == 1 {
			ans += f(j, n)
			ans %= MOD
		}
	}
	writeLine(ans)
}
  • C++
#define MOD 998244353
 
ll f(ll j, ll n) {
    ll p2=(1ll<<j);
    ll k=n/(2*p2);
    ll res=k*p2;
    ll l=n%(2*p2);
    res += max(0ll, l-p2+1);
    return res;
}
 
int main() {
    ll n, m; cin >> n >> m;
    ll ans = 0;
    rep(j, 0, 60) {
        if((m>>j)&1) {
            ans += f(j, n);
            ans %= MOD;
        }
    }
    cout << ans << endl;
}