luoguP6754 [BalticOI 2013 Day1] Palindrome-Free Numbers

luoguP6754 [BalticOI 2013 Day1] Palindrome-Free Numbers

簡述題意:

定義迴文串爲正着讀反着讀都同樣的數字串,若是一個數字串的一個長度大於 \(1\) 的子串也爲迴文串的話,那麼咱們也定義這個數字串爲迴文串。ios

因此不是迴文串的數字爲非迴文串,求區間 \([l, r]\) 內有多少個非迴文串 ,數據範圍: \(0 \le l \le r \le 10^{18}\)數組

Solution:

\(f[i][j][k]\) 表示長度爲 \(i\) 最高位爲 \(j\) 次高位爲 \(k\) 的非迴文串的個數函數

由於只要知足子串是迴文數它就是迴文串,因此判斷比較的時候只須要與前兩位數比較就好啦spa

轉移方程:code

\[f[i][j][k] = \sum_{k != j\ \And\And\ j != l\ \And\And\ l != k}f[i - 1][k][l] \]

實際意義:表示區間 \([jk000\cdots,jk999\cdots]\) 的非迴文數的和ci

對於求 \(ans_{l, r}\) 能夠轉化爲 \(ans_{1, r} - ans_{1, l - 1}\)get

\(ans_{l,r}\) 時的策略:string

\(len\)\(r\) 的位數,\(a[len]\) 中存 \(r\) 的每一位it

一、對於全部長度小於 \(len\)\(f\)\(ans += \sum_{1 \le j \le 9}^{2 \le i \le len - 1} f[i][j][k](0 \le k \le 9)\)io

二、對於長度小於等於 \(len\) 位且最高位小於 \(a[i]\)\(f\)\(ans += f[i][j][k] (0 \le k \le 9)\) ,加的過程當中注意判斷此時是不是非迴文串

三、由於 \(2\) 枚舉不到最後的個位數,因此要在最後單獨判斷一遍

本人在理解上的一個很傻逼的誤區:

主函數最後的那個 \(for\) 循環是判斷 \(l\) 是不是非迴文串,由於前面已經求了 \(ans_{1, r}\)\(ans_{1, l}\) ,二者相減所求區間是 \(ans_{l + 1, r}\) 並無取到 \(l\) ,按理說執行 \(solve(l - 1)\) 是能夠解決的,但由於這裏用的字符數組讀入,因此不免有鍋(我爲這理解了一下午

Code

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#define LL long long

using namespace std;
const int MAXN = 1010;

LL read(){
	LL s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar();	}
	while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
	return s * w;
} 

char l[MAXN], r[MAXN];
LL f[MAXN][13][13];
LL a[MAXN];

void init(){
	for(int i = 2; i <= 1001; ++i){
		for(int j = 0; j <= 9; ++j){
			for(int k = 0; k <= 9; ++k){
				if(j == k) continue;//若是相鄰兩個元素同樣,那麼必定是迴文串,直接跳過 
				for(int a = 0; a <= 9; ++a){
					if(j != a && a != k)//若是不是2迴文也不是3迴文 
						f[i][j][k] += f[i - 1][k][a];
				}
				if(i == 2) f[i][j][k]++;//若是長度爲2,必定不是 
			}
		}
	}
}
 
LL solve(char s[]){
	memset(a, 0, sizeof(a));
	LL tot = 0;
	bool t = 1;
	int len = strlen(s);
	LL ans = 0, last1 = -1, last2 = -1, sum = 0;
	for(int i = len; i >= 1; --i){
		a[i] = s[len - i] - '0';//把個位放前面 
		sum = (sum << 1) + (sum << 3) + a[i];//
	}
	if(len == 1) return sum + 1;//若是長度爲1,那麼不用判斷了 
	ans += 10;//算上長度爲1的那10個數 
	for(int i = 2; i < len; ++i){//老套路把滿着的先加上 
		for(int j = 1; j <= 9; ++j){
			for(int k = 0; k <= 9; ++k){
				ans = ans + f[i][j][k];
			}
		}
	}
	for(int i = len; i >= 2; i--){
		for(int j = 0; j < a[i]; ++j){
			if(i == len && j == 0) continue;//首位是0就跳過
			for(int k = 0; k <= 9; ++k){
				if(last1 != j && last2 != j && j != k && last1 != k){
					ans = ans + f[i][j][k];
				} 
			} 
		}
		if(last1 == a[i] || last2 == a[i]) {//上一位和上兩位 
			t = 0; break;
		} 
		last2 = last1, last1 = a[i];//更新 
	}
	if(t) {//若是沒有中途退出 
		for(int i = 0; i <= a[1]; ++i){//最後一位剩下的那一點 
			if(i != last1 && i != last2) ans++;
		}
	}
	return ans;
}

int main()
{
	init();
	cin >> l >> r;
	LL ans = solve(r) - solve(l);
	int t = strlen(l), flag = 0;
	for(int i = 1; i < t; ++i){
		if(l[i] == l[i - 1] || (l[i] == l[i - 2] && i > 1)) {
			flag = 1;
			break;
		}
	}
	if(!flag) ans++;
	printf("%lld", ans);
	return 0;
}
相關文章
相關標籤/搜索