CodeForces 215E Periodical Numbers 數位DP

題意:給你一個區間[l,r],求這個區間內滿足條件的數,條件是:這個數的二進制表示時,dig[i] == dig[i+k],(0<k<len,且len%k==0,len爲這個數的二進制代碼長度)

路:考慮[0,x]這個區間,若x的位數爲len,當數的長度 i 爲0~len-1時,則是無限制的,這時dp[i] = sum{2^(k-1)},k爲滿足條件的循環長度。而且還要去掉重複的,比如當長度爲6時,循環長度爲2,3的數均會重複計算,當數的長度爲len時,則在限制下,計算滿足條件的數,具體實現看代碼註釋:

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef __int64 int64;
typedef long long ll;
#define M 600005
#define N 1000005
#define max_inf 0x7f7f7f7f
#define min_inf 0x80808080
#define mod 1000000007
#define lc rt<<1
#define rc rt<<1|1

ll dp[70] , r , l , table[70];//table[i] = 2^i;
int dig[70];

ll Cal(int k)//計算長度爲k的數,滿足條件的個數
{
	int i , j;
	ll ret = 0;
	for (i = 1 ; i < k ; i++)
	{
		if (k%i)continue;
		dp[i] = table[i-1];
		for (j = 1 ; j < i ; j++)//減掉重複計算的數
		{
			if (i%j == 0)
				dp[i] -= dp[j];
		}
		ret += dp[i];
	}
	return ret;
}

ll Solve(ll k)
{
	int i , j , len = 0;
	ll ret = 0 , temp = k , num;
	while (temp)
	{
		dig[++len] = temp&1;
		temp >>= 1;
	}

	//計算無限制時滿足條件的數
	for (i = 1 ; i < len ; i++)ret += Cal(i);

	for (i = 1 ; i < len ; i++)//長度爲len時,枚舉循環長度
	{
		if (len%i)continue;

		num = 1;
		temp = 0;
		dp[i] = 0;
		for (j = len-1 ; j > len-i ; j--)
		{
			//若dig[j]==1則可以令dig[j]=0,轉變成無限制的情況
			if (dig[j])dp[i] += table[i-(len-j)-1];
			num = num*2+dig[j];
		}


		temp = num;
		int up = len/i;
		for (j = 1 ; j < up ; j++)num = (num<<i)+temp;
		//num保存的爲循環長度爲i,且循環內每一位都受限制的情況下的這個數
		dp[i] += (num <= k);//若num比k小,則加入答案中

		//去掉重複的計算的數
		for (j = 1 ; j < i ; j++)
		{
			if (i%j == 0)
				dp[i] -= dp[j];
		}

		ret += dp[i];
	}
	return ret;
}

int main()
{
	int i;
	for (table[0] = 1 , i = 1 ; i < 70 ; i++)table[i] = table[i-1]*2;
	while (~scanf("%I64d%I64d",&l,&r))
		printf("%I64d\n",Solve(r)-Solve(l-1));
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章