【CF809C】Find a car(Nim遊戲+sg函數+數位dp)

一眼看過去沒什麼思路…
先打一個2k2k2^k*2^k(這裏k=4k=4)的表:

  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
  2  1  4  3  6  5  8  7 10  9 12 11 14 13 16 15
  3  4  1  2  7  8  5  6 11 12  9 10 15 16 13 14
  4  3  2  1  8  7  6  5 12 11 10  9 16 15 14 13
  5  6  7  8  1  2  3  4 13 14 15 16  9 10 11 12
  6  5  8  7  2  1  4  3 14 13 16 15 10  9 12 11
  7  8  5  6  3  4  1  2 15 16 13 14 11 12  9 10
  8  7  6  5  4  3  2  1 16 15 14 13 12 11 10  9
  9 10 11 12 13 14 15 16  1  2  3  4  5  6  7  8
 10  9 12 11 14 13 16 15  2  1  4  3  6  5  8  7
 11 12  9 10 15 16 13 14  3  4  1  2  7  8  5  6
 12 11 10  9 16 15 14 13  4  3  2  1  8  7  6  5
 13 14 15 16  9 10 11 12  5  6  7  8  1  2  3  4
 14 13 16 15 10  9 12 11  6  5  8  7  2  1  4  3
 15 16 13 14 11 12  9 10  7  8  5  6  3  4  1  2
 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1

好整齊!仔細觀察就會發現如果把這個矩陣平均分成四塊,我們會發現左上角的那一塊和右下角的那一塊是完全一樣的,左下角的一塊和右上角的一塊剛好是另外兩塊每個數加2k12^{k-1},而且這四塊都滿足這個性質
所以如果第一個元素我們記爲a0,0a_{0,0}對於其中的一個元素ax,ya_{x,y},我們會發現當x,yx,y中有且僅有一個數的二進制的第k1k-1位爲11的時候,這個元素的值會加上2k12^{k-1},又因爲這個矩陣分成的四塊都滿足這個性質,所以可以直接把ax,ya_{x,y}分到相應的矩陣繼續算。仔細分析就可以得出ax,y=(xa_{x,y}=(x^y)+1y)+1
然後就是數位dp隨便搞搞就好了。

不打表怎麼做?(orzTJY大佬的做法)

每一個點求的是這個數所在的那一行左邊的和那一列上面的那些數的mexmex,看起來很像sgsg函數
一個點的sgsg值等於所有後繼狀態的sgsg值的mexmex,所以我們可以把一個點(x,y)(x,y)看做一個遊戲,他的所有後繼狀態是sg0...x1,ysg_{0...x-1,y}sgx,0...y1sg_{x,0...y-1}
我們會發現這其實就是一個Nim遊戲!對於一個點(x,y)(x,y),他其實等價於一個有兩堆石子分別有x,yx,y個的Nim遊戲(每次從一堆中取出任意個石子後就是後繼狀態),所以就可以算出(x,y)(x,y)sgsg值,這個sgsg值就是原矩陣每個數減1
根據Nim遊戲的性質就可以得到ax,y=(xa_{x,y}=(x^y)+1y)+1
然後數位dp就OK了。

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
int q,x_1,y_1,x_2,y_2,k,len,a[35],b[35],c[35];
pair<int,int>dp[35][2][2][2];
int Add(int a,int b){
	return a+b>=mod?a+b-mod:a+b;
}
int Minus(int a,int b){
	return a<b?a-b+mod:a-b;
}
pair<int,int>operator+(pair<int,int>a,pair<int,int>b){
	return make_pair(Add(a.first,b.first),Add(a.second,b.second));
}
pair<int,int>dfs(int now,int f1,int f2,int f3){
	if(now==-1)
		return make_pair(1,0);
	if(~dp[now][f1][f2][f3].first)
		return dp[now][f1][f2][f3];
	pair<int,int>ret=make_pair(0,0);
	for(int i=0;i<=(f1?a[now]:1);i++)
		for(int j=0;j<=(f2?b[now]:1);j++)
			if((i^j)<=(f3?c[now]:1)){
				pair<int,int>tmp=dfs(now-1,f1&&i==a[now],f2&&j==b[now],f3&&(i^j)==c[now]);
				ret=ret+tmp;
				if(i^j)
					ret.second=(ret.second+(1ll<<now)*tmp.first)%mod;
			}
	dp[now][f1][f2][f3]=ret;
	return ret;
}
int calc(int x,int y,int k){
	if(x<0||y<0||k<0)
		return 0;
	memset(dp,-1,sizeof(dp));
	len=0;
	while(x||y||k){
		a[len]=(x&1);
		b[len]=(y&1);
		c[len++]=(k&1);
		x>>=1;
		y>>=1;
		k>>=1;
	}
	pair<int,int>ret=dfs(len-1,1,1,1);
	return Add(ret.first,ret.second);
}
int main(){
	scanf("%d",&q);
	while(q--){
		scanf("%d%d%d%d%d",&x_1,&y_1,&x_2,&y_2,&k);
		printf("%d\n",Minus(Add(calc(x_2-1,y_2-1,k-1),calc(x_1-2,y_1-2,k-1)),Add(calc(x_1-2,y_2-1,k-1),calc(x_2-1,y_1-2,k-1))));
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章