1. 程式人生 > >【BZOJ3329】Xorequ 數位DP+矩陣乘法

【BZOJ3329】Xorequ 數位DP+矩陣乘法

cnblogs -- -1 ron 後來 sam [] led 矩陣

【BZOJ3329】Xorequ

Description

技術分享

Input

第一行一個正整數,表示數據組數據 ,接下來T行
每行一個正整數N

Output

2*T行
第2*i-1行表示第i個數據中問題一的解,

第2*i行表示第i個數據中問題二的解,

Sample Input

1
1

Sample Output

1
2

HINT

x=1與x=2都是原方程的根,註意第一個問題的解
不要mod 10^9+7

1<=N<=10^18
1<=T<=1000

題解:由於x*3中一位最多只會改動3位,所以我一開始想把所有情況都打個表出來,後來發現這個可以嚴格證明。

x^(3x)=2x -> x^(2x)=3x -> x^(2x)=x+2x -> x^(2x)=x^(2x)+((x&(2x))<<1) -> x&(2x)=0

也就是說x不能有相鄰2位都等於1,第一問直接數位DP即可,不過這個DP式好像就是斐波那契數列的遞推公式?所以第二問無腦矩乘即可。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=1000000007;
ll n;
ll f[70];
ll ans;
int v[70];
struct node
{
	ll a[5][5];
	node () {memset(a,0,sizeof(a));}
	ll * operator [] (int b) {return a[b];}
	node operator * (node b)
	{
		node c;
		for(int i=0;i<=1;i++)	for(int j=0;j<=1;j++)	for(int k=0;k<=1;k++)	c[i][j]=(c[i][j]+a[i][k]*b[k][j])%P;
		return c;
	}
}res,tr;
void pm(ll y)
{
	while(y)
	{
		if(y&1)	res=res*tr;
		tr=tr*tr,y>>=1;
	}
}
void work()
{
	scanf("%lld",&n);
	int i;
	ans=0;
	for(i=60;~i;i--)
	{
		if((1ll<<i)&n)
		{
			ans+=f[i+1];
			if((1ll<<(i+1))&n)	break;
		}
	}
	if(i<0)	ans++;
	printf("%lld\n",ans-1);
	tr[0][0]=tr[0][1]=tr[1][0]=1,tr[1][1]=0;
	res[0][1]=res[0][0]=1,res[1][0]=res[1][1]=0;
	pm(n);
	printf("%lld\n",res[0][0]);
}
void init()
{
	int i;
	f[1]=f[0]=1;
	for(i=2;i<=60;i++)	f[i]=f[i-1]+f[i-2];
}
int main()
{
	int T;
	scanf("%d",&T);
	init();
	while(T--)	work();
	return 0;
}

【BZOJ3329】Xorequ 數位DP+矩陣乘法