萌新求助数位dp
查看原帖
萌新求助数位dp
236862
Miraik楼主2022/1/14 22:19

RT,调了3天了,有注释,样例都过不去 /kel

问题应该出在统计答案上QaQ 不会调了QaQ

#include<bits/stdc++.h>
//#define int ll
#define pb push_back
#define mp make_pair
#define sec second
#define fir first
#define pii pair<int,int>
#define piii pair<int,pair<int,int> >
using namespace std;
typedef long long ll;
const int M=15;
const int N=10;
const int inf=1<<30;
const ll inff=1ll<<60;
inline ll read(){
	ll x=0;int f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
int m,p[M];ll l,r,f[M][N][N][2][2][2]; ll ans;
void init(){
	//f[i][j][k][l][t][p] 填到第 i 位 这位是 j 上位为 k 是否连 3,是否有 4/8  
	for(int i=0;i<=9;i++)
	    for(int j=0;j<=9;j++)
	        f[2][i][j][0][(!(i^4))|(!(j^4))][(!(i^8))|(!(j^8))]++;
	for(int i=0;i<=9;i++) //枚举前 3 位 
	    for(int j=0;j<=9;j++)
	        for(int k=0;k<=9;k++)
	            f[3][j][k][!((i^j)|(i^k))][(!(i^4))|(!(j^4))|(!(k^4))][(!(i^8))|(!(j^8))|(!(k^8))]++;
	for(int i=3;i<=12;i++) //转移 
	    for(int j=0;j<=9;j++)
	        for(int k=0;k<=9;k++)
	            for(int l=0;l<=9;l++)
	                for(int t3=0;t3<=1;t3++)
	                    for(int t4=0;t4<=1;t4++)
	                        for(int t8=0;t8<=1;t8++)
	                    	    f[i+1][j][k][t3|(!((j^k)|(k^l)))][t4|(!(j^4))][t8|(!(j^8))] += f[i][k][l][t3][t4][t8];
//	printf("%lld %lld %lld\n",f[5][6][6][1][0][0],f[5][5][2][1][0][1],f[8][7][3][1][1][0]);
}
ll solve(ll x){ //求小于 x 的答案个数 
	m=0;
	while(x)p[++m]=x%10,x/=10;
    ans=0;
    //统计答案 
    for(int i=3;i<m;i++) //小于 m 位 
        for(int j=1;j<=9;j++)
            for(int k=0;k<=9;k++)
                ans+=f[i][j][k][1][0][0]+f[i][j][k][1][0][1]+f[i][j][k][1][1][0];
    for(int i=1;i<p[m];i++) //第一位小 
        for(int j=0;j<=9;j++)
            ans += f[m][i][j][1][0][0]+f[m][i][j][1][0][1]+f[m][i][j][1][1][0];
    int ok_3=1,ok_4=(p[m]==4),ok_8=(p[m]==8);
	for(int i=m-1;i;i--){ //第一位一样,向后卡位 
	    for(int j=1;j<p[i];j++)
		    for(int k=0;k<=9;k++){ 
		        int in_3=1,in_4=0,in_8=0;
		        if(j==k && p[i+1]==j) in_3=0;
		        if(p[i+1]==j && j==p[i+2] && i+2<=m) in_3=0;
		        if(j==4 || k==4) in_4=1;
		        if(j==8 || k==8) in_8=1;
		        in_3 &= ok_3;
		        in_4 |= ok_4;
				in_8 |= ok_8; 
		        if(in_4 && in_8) continue;
		    	for(int l=1;l>=in_3;l--){ //已经出现过 3 个连续,那么 l 可以取 0 
		    		ans += f[i][j][k][l][0][0];
		    	    if(in_4) ans += f[i][j][k][l][1][0]; //出现过 4,后面不能有 8 
		    	    else if(in_8) ans += f[i][j][k][l][0][1]; //出现过 8,后面不能有 4 
		    	    else ans += f[i][j][k][l][1][0]+f[i][j][k][l][0][1];
		    	}
		    } 
		if(p[i] == p[i+1] && p[i+1] == p[i+2] && i+2<=m) ok_3=0;  
		if(p[i] == 4) ok_4=1;
		if(p[i] == 8) ok_8=1;
		if(ok_4 && ok_8) break; //这种情况肯定不行 
	} 
//	printf("answer:%lld\n",ans); 
	return ans;
}
int main(){int tests=1;//tests=read();
while(tests--){
	ll l=read(),r=read();
	init();
	printf("%lld\n",solve(r+1)-solve(l));
}	return 0;
}

2022/1/14 22:19
加载中...