hdu6156(数位dp)

题意:定义函数 f(n, k) 如果数n在k进制下是回文数则f(n, k) = k,否则f(n, k) = 1,求n在L,R区间内k在l,r区间内的函数和

问题转化为求L,R区间内k在l,r区间内的回文数的个数

对数位dp理解还是不够深刻,比赛的时候想到数位dp没敢开,赛后补题还是写不出来,不会设状态,看了网上博客定了3个状态,dp[i][j][k]表示在i进制下,长度为j,枚举到第k位时,回文串的个数, 其实数位dp的状态是要表示同一类型的数,在这题里的同一类型就是长度相同,进制相同,且同为回文串,主要还是状态太难想了

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

ll dp[100][100][50];
int num[100];
int nums[100];

ll dfs(int tot, int pos, int k, int lead, int limit)
{
	if (pos < 1) return 1;
	if (!limit && dp[k][tot][pos] != -1) return dp[k][tot][pos];
	int up = limit ? num[pos] : k-1;
	
	ll sum = 0;
	for (int i = 0; i <= up; i++)
	{
		nums[pos] = i;
		if (lead && i == 0) sum += dfs(tot-1, pos-1, k, lead, limit && i == up);
		else if (pos > tot / 2) sum += dfs(tot, pos-1, k, lead && i == 0, limit && i == up);//数的前一半可以取任意数字 
		else if (i == nums[tot-pos+1]) sum += dfs(tot, pos-1, k, lead && i == 0, limit && i == up);//数的后一半必须满足回文 
	}
	if (!limit) dp[k][tot][pos] = sum;
	return sum;
}

ll solve(int x, int k)
{
	int len = 0;
	while (x > 0)
	{
		num[++len] = x % k;
		x/= k;
	}
	return dfs(len, len, k, 1, 1);
}

int main()
{
	memset(dp, -1, sizeof(dp));
	int T, kase = 0;
	scanf("%d",&T);
	while (T--)
	{
		ll L, R, l, r;
		scanf("%lld%lld%lld%lld",&L,&R,&l,&r);
		ll sum = 0;
		for (int i = l; i <= r; i++)
		{
			ll ans = solve(R,i) - solve(L-1,i);
			sum += ans * i;
			sum += (R - L + 1 - ans);
		}
		printf("Case #%d: %lld\n",++kase,sum);
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/bpdwn2017/article/details/81320149
今日推荐