質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

1891閲覧

XORが0となる数列は何通りあるか?という問題が解けない

toshiyan

総合スコア74

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

1グッド

6クリップ

投稿2019/05/13 08:00

編集2019/05/14 00:51

競プロの問題で質問です。

長さNのK以下の数列で、XORを取ったものが0となる数列は何通りあるでしょうか。

たとえば、N=3, K=2のとき、(0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 1), (1, 1, 0), (2, 0, 2), (2, 2, 0) の7通りが答えです。

という問題を解きたいのですが、解くことができません。どのようにすれば解けるでしょうか?制約は N ≦ 1000, K ≦ 2^50 です。

追記

考えたことを追記します。

①__長さNのK以下の数列のXORが0の通り数__ (求めたいもの)
②__長さN-1のK以下の数列のXORがK以下の通り数__
③__長さN-1のK以下の数列のXORがKを超える通り数__

①と②は等しいです。
なぜなら、②のXORの結果と同じ数値を後ろに付け足せば、①の数列になるからです。
そして、③は②の余事象であるため、③を求めることを考えました。
以下のようなDPを考えます。

dp[i][j] := 上からiビット目まで見たときの通り数(jはKを超えたかどうか)

そして次のようなコードを書きました。
(オーバーフローは気にしない方向でお願いします)

cpp

1#include <bits/stdc++.h> 2using namespace std; 3 4int dp[52][2]; 5 6int comb(int x,int y){ 7 if(x<0||y<0||x<y)return 0; 8 int r=1; 9 for(int i=1;i<=y;++i){ 10 r*=(x-i+1); 11 r/=i; 12 } 13 return r; 14} 15 16int main(){ 17 int N; 18 long long K; 19 cin>>N>>K; 20 21 int k=0; 22 long long kk=K; 23 while(kk)k++,kk>>=1; 24 25 int odd=0,even=0; 26 for(int i=1;i<=N-1;i+=2){ 27 odd+=comb(N-1,i); 28 } 29 for(int i=0;i<=N-1;i+=2){ 30 even+=comb(N-1,i); 31 } 32 33 dp[0][0]=1; 34 for(int i=0;i<k;++i){ 35 for(int j=0;j<=1;++j){ 36 if(j==1){ 37 dp[i+1][j]+=dp[i][j]*(even+odd); 38 }else{ 39 if((K&(1LL<<(k-i-1)))==0){ 40 dp[i+1][1]+=dp[i][j]*odd; 41 dp[i+1][0]+=dp[i][j]*even; 42 }else{ 43 dp[i+1][j]+=dp[i][j]*odd; 44 } 45 } 46 } 47 } 48 cout<<pow(K+1,N-1)-dp[k][1]<<endl; 49 return 0; 50}

しかしこのコードでは、数列の最後の要素がKを超えた場合が考慮されていません。
たとえばN=3、K=2のとき、(1, 2, 3), (2, 1, 3)まで数えてしまうため、出力は5です。
どのように書き換えれば 数列の要素がKを超えた場合 を考慮できるかわからず、解決の糸口が見つからなかったため質問いたしました。

追記2

できればヒントだけでも教えていただけると嬉しいです。
ある程度の行間であれば頑張って対応いたします。
回答よろしくお願いします。

DrqYuto👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cateye

2019/05/13 08:29 編集

>解くことができません・・・何処までやったか、途中でいいのでソース貼り付けて下さい。でないと、丸投げになります。 また、環境(OSやコンパイラなど)も教えて下さい。
dodox86

2019/05/13 08:19

どこで提示されている問題でしょうか。今、競技開催中な訳ではないですよね。
xebme

2019/05/13 08:33

群論と関係ありますか?
xebme

2019/05/13 08:38

群論とは関係ありません。まず冪集合から見ていく。
xebme

2019/05/13 08:52

変なこと言ってごめんなさい。何通りとは数だけいえばよいですか?列挙できないですよね。
toshiyan

2019/05/13 09:45

列挙は難しいです。通り数は軽く10億を超えます…。
xebme

2019/05/13 11:40

2 ≦ N かつ 2^0 ≦ K ですか。K+1 が 2^m のとき②がなさそうですね。
toshiyan

2019/05/13 11:47

②ではなくて③な気がします。間違っていたらすみません…。
xebme

2019/05/13 11:51

③です。XOR計算せずに数列でできないか(③の件数が割り出せればね)
guest

回答3

0

自己解決

解決しましたのでコードを載せます。回答してくださった方々ありがとうございました!!

cpp

1#include <bits/stdc++.h> 2using namespace std; 3 4int dp[60][1010]; 5 6int comb(int x,int y){ 7 if(x<0||y<0||x<y)return 0; 8 int r=1; 9 for(int i=1;i<=y;++i){ 10 r*=(x-i+1); 11 r/=i; 12 } 13 return r; 14} 15 16int pow2(int n){ 17 int ret=1; 18 while(n>0)ret*=2,--n; 19 return ret; 20} 21 22int main(){ 23 int N; 24 long long K; 25 cin>>N>>K; 26 27 int D=0; 28 long long kk=K; 29 while(kk)D++,kk>>=1; 30 31 dp[0][0]=1; 32 for(int i=0;i<D;++i){ 33 for(int j=0;j<=N;++j){ 34 if(!(K&(1<<(D-i-1)))){ 35 dp[i+1][j]+=dp[i][j]*pow2(j-1); 36 continue; 37 } 38 for(int k=0;k<=N-j;++k){ 39 if(j==0&&(N-k)%2)continue; 40 dp[i+1][j+k]+=dp[i][j]*comb(N-j,k)*pow2(j-1); 41 } 42 } 43 } 44 int ans=0; 45 for(int i=0;i<=N;++i)ans+=dp[D][i]; 46 cout<<ans<<endl; 47 return 0; 48}

解説pdf

投稿2019/05/17 04:01

toshiyan

総合スコア74

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

前提
「K以下の数列のXORがKを超える」とその時点で、その数列の作成は終了する
これが正しければ、損失ゲーム(価値割引)ではないかと思います。

発見
K+1 = 2^m のとき③は起きないので、通り数は、(K+1)^(N-2) (これ計算できますか?)

損失
K=2 のとき、③は2件、②は7件
1回 XOR をとると (2+1)(2+1) のうち 2 件失う。
一般化すると、1回 XOR をとると (K+1)
(K+1) のうち Z 件失う。K にたいして Z はそれぞれ一定の数。
(K+1 = 2^m のとき Z=0)

K1234567
Z0206860

K から Z を計算する方法が見つかれば解決。

((K+1)*(K+1) - Z) * (K+1) - Z ...

だめですね。2段階目以降は工夫しないとうまくいきません。

追記1

マイナスをたくさんいただきましたので、ない知恵を絞って少し前進。

k=2の場合の分析

N12345
X ②+③92151123297X(n)=R(n-1)*(k+1)
Z ③24102458Z(n)=f( rx(n-1), k)
R ①=②7174199239R(n) = X(n) - Z(n)
Rの内訳 0 (r0)37174199r0(n)=R(n-1)
Rの内訳 0以外 (rx)4102458140

r0(n)=R(n-1)なのは質問内容からもあきらかです。
N-1 のときの R が求める数です。
0 から K を超える数は生じない。0 XOR a = a
0 を超える数から K を超える数が生じる。それをつくる関数を f としておきます。

f( rx(n-1), k) を求めたい。

  • k=2のとき、rx(n-1)
  • k+1=2^m のとき 0

投稿2019/05/13 21:29

編集2019/05/14 04:01
xebme

総合スコア1083

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

npkk

2019/05/14 04:41

競技プログラミングがどういうものか調べてから回答された方がよろしいかと思います。回答が文脈に適合していません。
xebme

2019/05/14 09:05

アドバイスありがとうございます。勉強して出直します。質問者様、大変失礼いたしました。
guest

0

ヒントだけでも良いとの事なので置いておきます。
なんとなく再帰処理っぽい気はするのですが……。
イメージ説明
【追記】
表は[4,0,1,5]とかを、4015(7進数)などに見立てて数を調べた結果です。
プログラムは以下。

VBA

1'注意:下手にketaとかsinsuに大きな数字(10以上とか)を入れると、あっという間に固まります。 2Sub Macro1() 3 Dim keta As Double 4 Dim sinsu As Double 5 Dim ans As Double 6 7 Dim i As Double 8 Dim mstr_i As String 9 Dim tmpsinsu As Double 10 11 keta = 3 12 sinsu = 4 13 14 ans = 0 15 i = 0 16 While i < sinsu ^ keta 17 mstr_i = S10_2(i, sinsu) 18 tmpsinsu = 0 19 mstr_i = Replace(mstr_i, "0", "") '先に0を抜いとく 20 While Len(mstr_i) Mod 2 = 0 And Len(mstr_i) <> 0 21 mstr_i = Replace(mstr_i, CStr(tmpsinsu), "") 22 tmpsinsu = tmpsinsu + 1 23 Wend 24 If Len(mstr_i) = 0 Then 25 ans = ans + 1 26 End If 27 i = i + 1 28 Wend 29 30 MsgBox "sinsu=" & sinsu & " keta=" & keta & " ans=" & ans 31End Sub 32 33'10進→2進 34Function S10_2(X As Double, sin As Double) As String 35 Dim XX As Double 36 Dim Y As String 37 XX = X 38 If (XX < 0) Then 39 Y = "Error" 40 Else 41 Do 42 Y = XX Mod sin & Y 43 XX = XX \ sin 44 Loop While (XX > 0) 45 End If 46 S10_2 = Y 47End Function

投稿2019/05/14 06:15

編集2019/05/14 06:23
torisan

総合スコア678

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問