My_Plan part1 小结
数位DP AC十道题目以上 成就达成
八月份!三个月!想想就令人兴奋呢
开始写总结啦
貌似简单的数位DP只需要改改模板就可以啦
就按照我的做题顺序开始总结吧
先是学习了一发模板:http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html
但是一开始学的不是很深刻,导致后来做题的时候犯了很多错误
hdu 2089
数字中不能出现62和4
一开始设计的状态是f[i][j]表示长度为i且上一个数字为j,然后写了一发过来
其实状态是可以精简的,可以精简成f[i][0/1]表示长度为i上一个数字是否为6
精简后的状态比裸DP快的多,精简后的代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std; int L,R;
int f[12][2];//0 无6 1 有6
int Num[12],len=0;
void check(int n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
int DFS(int pos,int six,int flag){
if(!pos)return 1;
if(!flag&&f[pos][six]!=-1)return f[pos][six];
int tmp=0,u=flag?Num[pos]:9;
for(int i=0;i<=u;++i){
if(i==4)continue;
if(six&&i==2)continue;
tmp+=DFS(pos-1,i==6,flag&&i==u);
}return flag?tmp:f[pos][six]=tmp;
}
int main(){
while(scanf("%d%d",&L,&R)==2){
if(!L&&!R)break;
check(R);memset(f,-1,sizeof(f));
R=DFS(len,0,1);
check(L-1);memset(f,-1,sizeof(f));
L=DFS(len,0,1);
printf("%d\n",R-L);
}return 0;
}
hdu 3555
数字中不能出现49,跟上道题目做法一样
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std; typedef long long LL;
LL n;
LL f[22][2];//0 不是4 1 是4
int T;
int Num[22],len; void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
LL DFS(int pos,int four,int flag){
if(!pos)return 1;
if(!flag&&f[pos][four]!=-1)return f[pos][four];
LL tmp=0;
int u=flag?Num[pos]:9;
for(int i=0;i<=u;++i){
if(four&&i==9)continue;
tmp+=DFS(pos-1,i==4,flag&&i==u);
}return flag?tmp:f[pos][four]=tmp;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%lld",&n);
check(n);memset(f,-1,sizeof(f));
printf("%lld\n",n+1-DFS(len,0,1));
}return 0;
}
hdu 3652
数字中要求有13这个子串同时自身能被13整除
加一维0/1/2表示当前数字和13的匹配长度
再加一维记录余数即可
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std; int n;
int t[13];
int f[13][13][3];
int Num[13],len=0; void check(int n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
int DFS(int pos,int mod,int one,int flag){
if(!pos){
if(one==2&&!mod)return 1;
return 0;
}
if(!flag&&f[pos][mod][one]!=-1)return f[pos][mod][one];
int tmp=0,u=flag?Num[pos]:9;
for(int i=0;i<=u;++i){
int now=one;
if(now!=2){
if(now==1&&i==3)now=2;
else if(i==1)now=1;
else now=0;
}
tmp+=DFS(pos-1,(mod+i*t[pos])%13,now,flag&&i==u);
}return flag?tmp:f[pos][mod][one]=tmp;
} int main(){
t[1]=1;
for(int i=2;i<=10;++i)t[i]=t[i-1]*10;
while(scanf("%d",&n)==1){
check(n);memset(f,-1,sizeof(f));
n=DFS(len,0,0,1);
printf("%d\n",n);
}return 0;
}
codeforces #55 D
求能被自己各位非零数字整除的数字
我们发现我们需要记录这个数字%(1-9)的值,如果强行加维会爆炸
但是我们发现对于%p来说,假设我们知道一个数字%(k*p)的值,我们就可以知道其%p的值
那么我们只需要记录这个数字%2520的余数就可以了(LCM(1-9)=2520)
之后我们需要知道这个数字中那些数字出现过,在这里其实我们只需要知道2-9是否出现过就可以了
可以在加一维状态压缩来记录
更好一点的做法是我们发现%2=0和%5=0我们只需要判断最后一位数字就可以了
而去掉2和5之后的LCM=252,这样我们就可以把状态数变成了原来的1/10
但是本人还是写的2520的QAQ
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std; typedef long long LL;
int T;
int Num[22],len=0;
LL f[22][2520][256];
LL t[22];
LL L,R; void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
LL DFS(int pos,int mod,int S,int flag){
if(!pos){
for(int i=0;i<8;++i){
if(S>>i&1){
if(mod%(i+2))return 0;
}
}return 1;
}
if(!flag&&f[pos][mod][S]!=-1)return f[pos][mod][S];
LL tmp=0;
int u=flag?Num[pos]:9;
for(int i=0;i<=u;++i){
int now=S;
if(i>1)now|=(1<<(i-2));
tmp=tmp+DFS(pos-1,(mod+i*t[pos])%2520,now,flag&&i==u);
}return flag?tmp:f[pos][mod][S]=tmp;
} int main(){
t[1]=1;
for(int i=2;i<=20;++i)t[i]=t[i-1]*10;
scanf("%d",&T);
memset(f,-1,sizeof(f));
while(T--){
scanf("%lld%lld",&L,&R);
check(R);
R=DFS(len,0,0,1);
check(L-1);
L=DFS(len,0,0,1);
printf("%lld\n",R-L);
}return 0;
}
poj 3252
求有多少个二进制串中0的数量不少于1的数量
状态是很显然的,f[i][j]表示长度为i,1的数量为j
然后直接裸上DP就可以了,注意此时前导零对答案会有影响
所以我采用的方法是<len的直接预处理计算
=len的采用记忆化搜索
其实可以直接记忆化搜索,在多传一个参数表示是否是首位就可以啦
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std; int L,R;
int Num[35],len=0;
int f[35][35],ans;
int dp[35][35]; void check(int n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=(n&1),n>>=1;
}
int DFS(int pos,int num_one,int flag){
if(!pos){
if(num_one<=(len>>1))return 1;
return 0;
}
if(!flag&&f[pos][num_one]!=-1)return f[pos][num_one];
int tmp=0,u=flag?Num[pos]:1;
for(int i=(pos==len?1:0);i<=u;++i){
tmp=tmp+DFS(pos-1,num_one+(i==1),flag&&i==u);
}
return flag?tmp:f[pos][num_one]=tmp;
} int main(){
scanf("%d%d",&L,&R);
dp[1][1]=1;
for(int i=1;i<32;++i){
for(int j=0;j<=i;++j){
dp[i+1][j+1]+=dp[i][j];
dp[i+1][j]+=dp[i][j];
}
}
check(R);memset(f,-1,sizeof(f));
R=DFS(len,0,1);
for(int i=1;i<len;++i){
for(int j=0;j<=(i>>1);++j)R+=dp[i][j];
}
check(L-1);memset(f,-1,sizeof(f));
L=DFS(len,0,1);
for(int i=1;i<len;++i){
for(int j=0;j<=(i>>1);++j)L+=dp[i][j];
}
printf("%d\n",R-L);
return 0;
}
hdu 3709
定义平衡数是存在一个重心,使得左右连边的权重和相等的数
求给定区间内有多少个平衡数
首先我们可以证明在没有前导零的情况下,一个平衡数最多只有一个重心
因为重心移动一定是一边加上一个正整数,另一边减去一个正整数,不可能存在继续平衡的可能性
那么由于位数很小,我们不妨枚举重心,我们发现实际上可能的权重和也很小
就设f[i][j]表示位数为i,权重和为j的方案就可以啦
然后如果考虑不考虑前导零的话,最后还要减去000000这种情况
由于L>=0,所以要特判L=0的情况
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std; typedef long long LL;
int T,now,cur;
LL L,R;
LL f[22][3010];
int Num[22],len=0; void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
LL DFS(int pos,int sum,int flag){
if(!pos){
if(sum==0)return 1;
return 0;
}
if(!flag&&f[pos][sum]!=-1)return f[pos][sum];
LL tmp=0;int u=flag?Num[pos]:9;
for(int i=0;i<=u;++i){
int QAQ=sum+i*(pos-now);
if(QAQ<0)continue;
tmp+=DFS(pos-1,QAQ,flag&&i==u);
}return flag?tmp:f[pos][sum]=tmp;
} int main(){
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&L,&R);
check(R);R=0;cur=len;
for(int i=1;i<=len;++i){
now=i;
memset(f,-1,sizeof(f));
R+=DFS(len,0,1);
}
if(L==0)L=0,len=1;
else{
check(L-1);L=0;
for(int i=1;i<=len;++i){
now=i;
memset(f,-1,sizeof(f));
L+=DFS(len,0,1);
}
}printf("%lld\n",R-L-(cur-len));
}return 0;
}
SPOJ BALNUM
求有多少个数字出现的每个偶数数字出现了奇数次,出现的每个奇数数字出现了偶数次
我们发现一个数字最多只有3种情况,没出现,出现奇数次,出现偶数次
用三进制表示状态压缩即可,自己的代码写的略丑
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
using namespace std; typedef long long LL;
int T;
int Num[22],len=0;
bool vis[60010];
int code[12];
LL f[22][60010];
LL L,R; //0 没出现 1 出现奇数 2 出现偶数 void decode(int S,int *code){
for(int i=0;i<=9;++i){
code[i]=S%3;S/=3;
}return;
}
int encode(int *code){
int S=0;
for(int i=9;i>=0;--i)S=S*3+code[i];
return S;
}
bool judge(int S){
decode(S,code);
for(int i=0;i<=9;++i){
if(i&1){
if(code[i]==1)return false;
}else if(code[i]==2)return false;
}return true;
}
void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
LL DFS(int pos,int S,int flag,int first){
if(!pos){
if(vis[S])return 1;
return 0;
}
if(!flag&&f[pos][S]!=-1)return f[pos][S];
LL tmp=0;int u=flag?Num[pos]:9;
int ch[12];
for(int i=0;i<=u;++i){
decode(S,ch);
if(i==0&&first);
else{
if(ch[i]==0)ch[i]=1;
else if(ch[i]==1)ch[i]=2;
else ch[i]=1;
}
tmp+=DFS(pos-1,encode(ch),flag&&i==u,first&&i==0);
}return flag?tmp:f[pos][S]=tmp;
} int main(){
scanf("%d",&T);
for(int i=0;i<59049;++i)if(judge(i))vis[i]=true;
while(T--){
scanf("%lld%lld",&L,&R);
check(R);memset(f,-1,sizeof(f));
R=DFS(len,0,1,1);
check(L-1);memset(f,-1,sizeof(f));
L=DFS(len,0,1,1);
printf("%lld\n",R-L);
}return 0;
}
BZOJ 1026
windy数,没什么好说的
只是实验一下新模板的效果
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std; int L,R;
int f[12][12];
int dp[12][12];
int Num[12],len=0; void check(int n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
int DFS(int pos,int la,int flag){
if(!pos)return 1;
if(!flag&&f[pos][la]!=-1)return f[pos][la];
int tmp=0,u=flag?Num[pos]:9;
for(int i=(pos==len?1:0);i<=u;++i){
if(pos!=len&&abs(i-la)<2)continue;
tmp+=DFS(pos-1,i,flag&&i==u);
}return flag?tmp:f[pos][la]=tmp;
} int main(){
scanf("%d%d",&L,&R);
for(int i=1;i<=9;++i)dp[1][i]=1;
for(int i=1;i<10;++i){
for(int j=0;j<=9;++j){
if(dp[i][j]){
for(int k=0;k<=9;++k){
if(abs(j-k)<2)continue;
dp[i+1][k]+=dp[i][j];
}
}
}
}
memset(f,-1,sizeof(f));
check(R);R=DFS(len,0,1);
for(int i=1;i<len;++i)for(int j=0;j<=9;++j)R+=dp[i][j];
check(L-1);L=DFS(len,0,1);
for(int i=1;i<len;++i)for(int j=0;j<=9;++j)L+=dp[i][j];
printf("%d\n",R-L);
return 0;
}
BZOJ 4521
CQOI的模板题目,限定11位真是兹磁啊,留下了L=10^11的大陷阱
加一维表示上一个数是多少
再加一维0/1/2/3表示连续的数字个数
再加一维表示是否有8,再加一维表示是否有4
模板写起来就好啦
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cstdlib>
#include<algorithm>
using namespace std; typedef long long LL;
int Num[13],len=0;
LL L,R;
LL f[13][10][4][2][2];// 位数 0/1/2/3 是否有8 是否有4 void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
LL DFS(int pos,int la,int go_num,int eight,int four,int flag){
if(!pos){
if((go_num!=3)||(eight&&four))return 0;
return 1;
}
if(!flag&&f[pos][la][go_num][eight][four]!=-1)return f[pos][la][go_num][eight][four];
LL tmp=0;int u=flag?Num[pos]:9;
for(int i=(pos==len?1:0);i<=u;++i){
int now=go_num;
if(now==3);
else{
if(i==la)now++;
else now=1;
}
tmp+=DFS(pos-1,i,now,eight|(i==8),four|(i==4),flag&&i==u);
}return flag?tmp:f[pos][la][go_num][eight][four]=tmp;
} int main(){
scanf("%lld%lld",&L,&R);
memset(f,-1,sizeof(f));
check(R);R=DFS(len,0,0,0,0,1);
check(L-1);
if(len==11)L=DFS(len,0,0,0,0,1);
else L=0;
printf("%lld\n",R-L);
return 0;
}
hdu 4734
一开始被吓到了,开始考虑如何暴力的时候很惊讶的发现F(x)的范围很小,不到20000
那么我们缀一维表示F(x)的值就可以啦
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std; typedef long long LL;
int T,fit,A,B,kase;
int f[12][20010];
int Num[12],len=0; void check(int n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
int DFS(int pos,int sum,int flag){
if(!pos)return sum>=0;
if(sum<0)return 0;
if(!flag&&f[pos][sum]!=-1)return f[pos][sum];
int tmp=0,u=flag?Num[pos]:9;
for(int i=0;i<=u;++i){
tmp+=DFS(pos-1,sum-i*(1<<(pos-1)),flag&&i==u);
}return flag?tmp:f[pos][sum]=tmp;
} int main(){
scanf("%d",&T);
memset(f,-1,sizeof(f));
while(T--){
scanf("%d%d",&A,&B);kase++;
check(A);fit=0;
for(int i=1;i<=len;++i)fit=fit+Num[i]*(1<<(i-1));
check(B);B=DFS(len,fit,1);
printf("Case #%d: %d\n",kase,B);
}return 0;
}
hdu 4507
不能有7,转移的时候直接判
缀两维表示两个限制%7的余数
唯一的问题是求平方和
式子参照我在cojs上出的题目就可以啦
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std; typedef long long LL;
const int mod=1e9+7;
int T;
int Num[22],len=0;
LL L,R;
LL t[22];
struct num{
LL s0,s1,s2;
void clear(){s0=s1=s2=0;}
}f[22][7][7]; void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
num DFS(int pos,int num_mod,int sum_mod,int flag){
if(!pos){
num tmp;tmp.clear();
if(num_mod!=0&&sum_mod!=0)tmp.s0=1;
else tmp.s0=0;
return tmp;
}
if(!flag&&f[pos][num_mod][sum_mod].s0!=-1)return f[pos][num_mod][sum_mod];
num tmp;tmp.clear();
int u=flag?Num[pos]:9;
for(int i=0;i<=u;++i){
if(i==7)continue;
num now=DFS(pos-1,(num_mod+i*t[pos])%7,(sum_mod+i)%7,flag&&i==u);
LL sum=i*t[pos]%mod;
tmp.s0=tmp.s0+now.s0;if(tmp.s0>=mod)tmp.s0-=mod; tmp.s1=tmp.s1+now.s1;if(tmp.s1>=mod)tmp.s1-=mod;
tmp.s1=tmp.s1+now.s0*sum%mod;if(tmp.s1>=mod)tmp.s1-=mod; tmp.s2=tmp.s2+now.s2;if(tmp.s2>=mod)tmp.s2-=mod;
tmp.s2=tmp.s2+now.s1*sum%mod*2%mod;if(tmp.s2>=mod)tmp.s2-=mod;
tmp.s2=tmp.s2+now.s0*sum%mod*sum%mod;if(tmp.s2>=mod)tmp.s2-=mod;
}return flag?tmp:f[pos][num_mod][sum_mod]=tmp;
} int main(){
t[1]=1;
for(int i=2;i<=20;++i)t[i]=t[i-1]*10;
scanf("%d",&T);
memset(f,-1,sizeof(f));
while(T--){
scanf("%lld%lld",&L,&R);
check(R);num A=DFS(len,0,0,1);
check(L-1);num B=DFS(len,0,0,1);
printf("%lld\n",(A.s2-B.s2+mod)%mod);
}return 0;
}
hdu 4352
求[L,R]中有多少个数字的LIS恰好为K
首先注意到LIS最大是10,我们考虑如何求LIS
我们nlogn求LIS的时候对于每个长度记录最小的结尾即可
这样我们就可以状态压缩了
压缩的原理是这样的:如果某个数字i对应压缩位为1,那么[0,i]中有多少个1就是长度,这个数字表示这个长度的最小结尾
每次数位DP增加一个数字后更新即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std; typedef long long LL;
int T,k,kase;
int Num[22],len=0;
int num[1024];
LL L,R;
LL f[22][1024][11]; void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
void encode(int &S,int pos){
for(int i=pos;i<=9;++i){
if(S>>i&1){S^=(1<<i);break;}
}S|=(1<<pos);return;
}
LL DFS(int pos,int S,int flag,int first){
if(!pos)return num[S]==k;
if(!flag&&f[pos][S][k]!=-1)return f[pos][S][k];
LL tmp=0;int u=flag?Num[pos]:9;
for(int i=0;i<=u;++i){
int now=S;
if(first&&i==0);
else encode(now,i);
tmp+=DFS(pos-1,now,flag&&i==u,first&&i==0);
}return flag?tmp:f[pos][S][k]=tmp;
} int main(){
scanf("%d",&T);
for(int i=1;i<1024;++i)num[i]=num[i>>1]+(i&1);
memset(f,-1,sizeof(f));
while(T--){
scanf("%lld%lld",&L,&R);
scanf("%d",&k);kase++;
check(R);R=DFS(len,0,1,1);
check(L-1);L=DFS(len,0,1,1);
printf("Case #%d: %lld\n",kase,R-L);
}return 0;
}
ZOJ 3494
给定一个把十进制数字转化的方法
之后给定若干的禁止串,求[L,R]中不含禁止串的数字有多少个
建立AC自动机,之后设f[i][j]表示走了i步走到了AC自动机的j节点
预处理判断是否合法就可以了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<queue>
using namespace std; typedef long long LL;
const int maxn=2010;
const int mod=1000000009;
int T,n,len;
char s[maxn];
char Num[maxn];
int Go[maxn][10];
LL f[210][maxn];
LL L,R;
queue<int>Q;
struct trie{
int cnt;
int next[maxn][2];
int fail[maxn];
bool vis[maxn];
void init(){
cnt=1;fail[1]=0;vis[1]=false;
next[1][0]=next[1][1]=0;
}
int Newnode(){
++cnt;next[cnt][0]=next[cnt][1]=0;
fail[cnt]=0;vis[cnt]=false;return cnt;
}
void insert(){
int L=strlen(s+1),now=1;
for(int i=1;i<=L;++i){
int nxt=s[i]-'0';
if(!next[now][nxt])next[now][nxt]=Newnode();
now=next[now][nxt];
}vis[now]=true;return;
}
void build_fail(){
Q.push(1);fail[1]=0;
while(!Q.empty()){
int u=Q.front();Q.pop();
vis[u]|=vis[fail[u]];
for(int i=0;i<2;++i){
int k=fail[u];
while(k&&!next[k][i])k=fail[k];
if(next[u][i]){
fail[next[u][i]]=k?next[k][i]:1;
Q.push(next[u][i]);
}else next[u][i]=k?next[k][i]:1;
}
}return;
}
int Let_Go(int now,int num){
if(vis[now])return -1;
for(int i=3;i>=0;--i){
now=next[now][num>>i&1];
if(vis[now])return -1;
}return now;
}
}AC;
void Get_Pre(){
memset(f,-1,sizeof(f));
for(int i=1;i<=AC.cnt;++i){
for(int j=0;j<=9;++j){
Go[i][j]=AC.Let_Go(i,j);
}
}return;
}
void flip(){for(int i=1;i<=len;++i)Num[len-i+1]=s[i]-'0';}
LL DFS(int pos,int S,int flag,int first){
if(!pos)return 1;
if(!flag&&!first&&f[pos][S]!=-1)return f[pos][S];
LL ans=0;
if(first){
ans+=DFS(pos-1,S,flag&&Num[pos]==0,true);
if(ans>=mod)ans-=mod;
}else{
if(Go[S][0]!=-1)ans+=DFS(pos-1,Go[S][0],flag&&Num[pos]==0,false);
if(ans>=mod)ans-=mod;
}
int u=flag?Num[pos]:9;
for(int i=1;i<=u;++i){
if(Go[S][i]!=-1){
ans+=DFS(pos-1,Go[S][i],flag&&i==u,false);
if(ans>=mod)ans-=mod;
}
}return (flag||first)?ans:f[pos][S]=ans;
} int main(){
scanf("%d",&T);
while(T--){
AC.init();
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%s",s+1);
AC.insert();
}AC.build_fail();
Get_Pre();
scanf("%s",s+1);
len=strlen(s+1);
for(int i=len;i>=1;--i){
if(s[i]>'0'){s[i]--;break;}
s[i]='9';
}
flip();L=DFS(len,1,1,1);
scanf("%s",s+1);
len=strlen(s+1);
flip();R=DFS(len,1,1,1);
printf("%lld\n",(R-L+mod)%mod);
}return 0;
}
BZOJ 3530
上面那道题的弱化版,直接写就可以啦
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std; typedef long long LL;
const int maxn=1510;
const int mod=1e9+7;
int n,len;
char s[maxn];
char p[maxn];
char Num[maxn];
queue<int>Q;
int Go[maxn][10];
LL f[maxn][maxn];
LL ans;
struct trie{
int cnt;
int next[maxn][10];
int fail[maxn];
bool vis[maxn];
void init(){cnt=1;}
void insert(){
int L=strlen(p+1),now=1;
for(int i=1;i<=L;++i){
int nxt=p[i]-'0';
if(!next[now][nxt])next[now][nxt]=++cnt;
now=next[now][nxt];
}vis[now]=true;return;
}
void build_fail(){
Q.push(1);
while(!Q.empty()){
int u=Q.front();Q.pop();
vis[u]|=vis[fail[u]];
for(int i=0;i<10;++i){
int k=fail[u];
while(k&&!next[k][i])k=fail[k];
if(next[u][i]){
fail[next[u][i]]=k?next[k][i]:1;
Q.push(next[u][i]);
}else next[u][i]=k?next[k][i]:1;
}
}return;
}
LL DFS(int pos,int S,int flag,int first){
if(!pos)return 1;
if(!flag&&!first&&f[pos][S]!=-1)return f[pos][S];
LL tmp=0;
if(first){
tmp+=DFS(pos-1,S,flag&&Num[pos]==0,1);
if(tmp>=mod)tmp-=mod;
}else{
if(!vis[next[S][0]]){
tmp+=DFS(pos-1,next[S][0],flag&&Num[pos]==0,0);
if(tmp>=mod)tmp-=mod;
}
}
int u=flag?Num[pos]:9;
for(int i=1;i<=u;++i){
if(!vis[next[S][i]]){
tmp+=DFS(pos-1,next[S][i],flag&&i==Num[pos],0);
if(tmp>=mod)tmp-=mod;
}
}return (flag||first)?tmp:f[pos][S]=tmp;
}
}AC;
void flip(){for(int i=1;i<=len;++i)Num[len-i+1]=s[i]-'0';}
int main(){
scanf("%s",s+1);
AC.init();scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%s",p+1);
AC.insert();
}AC.build_fail();
len=strlen(s+1);
memset(f,-1,sizeof(f));
flip();ans=AC.DFS(len,1,1,1);
printf("%lld\n",(ans-1+mod)%mod);
return 0;
}
BZOJ 3209
枚举1的个数然后做数位DP,之后快速幂乘起来就好啦
值得一提的是这个题可以直接用组合数算数位DP的结果
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std; typedef long long LL;
const int mod=10000007;
LL n,ans;
LL f[72][72];
int Num[72],len=0;
LL pow_mod(LL v,LL p){
LL tmp=1;
while(p){
if(p&1)tmp=tmp*v%mod;
v=v*v%mod;p>>=1;
}return tmp;
}
void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=(n&1),n>>=1;
}
LL DFS(int pos,int one,int flag){
if(one<0)return 0;
if(!pos){
if(!one)return 1;
return 0;
}
if(!flag&&f[pos][one]!=-1)return f[pos][one];
LL tmp=0;int u=flag?Num[pos]:1;
for(int i=0;i<=u;++i){
tmp=tmp+DFS(pos-1,one-(i==1),flag&&i==u);
}return flag?tmp:f[pos][one]=tmp;
} int main(){
scanf("%lld",&n);
check(n);ans=1;memset(f,-1,sizeof(f));
for(int i=1;i<=len;++i){
LL now=DFS(len,i,1);
ans=ans*pow_mod(1LL*i,now)%mod;
}printf("%lld\n",ans);
return 0;
}
BZOJ 3329
x^3x=2x等价于x^2x=3x
我们知道异或是不进位加法
又因为x+2x=3x
所以当且仅当满足x中任意相邻两个数不都是1才是方程的一组解
即x&(x<<1)=0
对于第一问我们直接做数位DP就可以了
第二问我们设f[i][0/1]表示i位且上一位是0/1
f[i][0]=f[i-1][0]+f[i-1][1]
f[i][1]=f[i-1][0]
之后直接矩阵乘法就可以了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std; typedef long long LL;
const int mod=1e9+7;
int T;
int Num[72],len=0;
LL f[72][2];//上一位是多少
LL n,sum;
struct Matrix{
LL a[2][2];
Matrix(){memset(a,0,sizeof(a));}
}A,ans;
void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=(n&1),n>>=1;
}
LL DFS(int pos,int la,int flag){
if(!pos)return 1;
if(!flag&&f[pos][la]!=-1)return f[pos][la];
LL tmp=0;int u=flag?Num[pos]:1;
for(int i=0;i<=u;++i){
if(la==1&&i==1)continue;
tmp=tmp+DFS(pos-1,i,flag&&i==u);
}return flag?tmp:f[pos][la]=tmp;
}
void build_Matrix(){
A.a[0][0]=1;A.a[0][1]=1;
A.a[1][0]=1;A.a[1][1]=0;
}
Matrix operator *(const Matrix &A,const Matrix &B){
Matrix C;
for(int i=0;i<2;++i){
for(int j=0;j<2;++j){
for(int k=0;k<2;++k){
C.a[i][j]=C.a[i][j]+A.a[i][k]*B.a[k][j]%mod;
if(C.a[i][j]>=mod)C.a[i][j]-=mod;
}
}
}return C;
}
Matrix pow_mod(Matrix v,LL p){
Matrix tmp;
for(int i=0;i<2;++i)tmp.a[i][i]=1;
while(p){
if(p&1)tmp=tmp*v;
v=v*v;p>>=1;
}return tmp;
} int main(){
scanf("%d",&T);
memset(f,-1,sizeof(f));
build_Matrix();
while(T--){
scanf("%lld",&n);
check(n);
sum=DFS(len,0,1);
printf("%lld\n",sum-1);
ans.a[0][0]=1;ans.a[0][1]=0;
ans=ans*pow_mod(A,n);
printf("%lld\n",(ans.a[0][1]+ans.a[0][0])%mod);
}return 0;
}
hdu 3943
求(L,R]第k个nya数,nya数定义为恰好有x个4和y个7的数
我们二分之后问题转化成了数位DP,直接做就可以了,略坑的是这个区间是左开右闭的
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std; typedef long long LL;
int T,x,y,n,kase;
int Num[22],len=0;
LL P,Q,L,R,k;
LL f[22][22][22]; void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
LL DFS(int pos,int four,int seven,int flag){
if(four<0||seven<0)return 0;
if(!pos){
if(!four&&!seven)return 1;
return 0;
}
if(!flag&&f[pos][four][seven]!=-1)return f[pos][four][seven];
LL tmp=0;int u=flag?Num[pos]:9;
for(int i=0;i<=u;++i){
tmp=tmp+DFS(pos-1,four-(i==4),seven-(i==7),flag&&i==u);
}return flag?tmp:f[pos][four][seven]=tmp;
} int main(){
scanf("%d",&T);
memset(f,-1,sizeof(f));
while(T--){
scanf("%lld%lld",&P,&Q);
scanf("%d%d",&x,&y);kase++;
check(P);L=DFS(len,x,y,1);
check(Q);R=DFS(len,x,y,1);
printf("Case #%d:\n",kase);
scanf("%d",&n);
while(n--){
scanf("%lld",&k);
if(R-L<k){printf("Nya!\n");continue;}
LL l=P,r=Q;
while(l<r){
LL mid=(l+r)>>1;
check(mid);
LL ans=DFS(len,x,y,1);
if(ans-L<k)l=mid+1;
else r=mid;
}printf("%lld\n",r);
}
}return 0;
}
BZOJ 2757
求[L,R]中各位数字的乘积为k的数有多少个
首先我们会发现k只会有2,3,5,7这4个素因子
而进一步我们很容易发现满足这个条件的k是很少的
那么我们可以把满足条件的k哈希掉,数位DP即可
注意当k=0时,数位中只要至少有一个0就可以了,我采取的处理方法是又写了另外一个数位DP
这道题目我的代码略丑,其实一开始处理出来所以合法的k是最好的,在中间过程处理的话会变麻烦
至于求数字和,都求过平方和了这就随意做了
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std; typedef long long LL;
const int mod=20120427;
const int maxn=300010;
const int MOD=1333331;
int T;
LL L,R,k;
LL t[22];
struct num{
LL s0,s1;
void clear(){s0=s1=0;}
}dp[22][2],f[22][maxn],A,B;
int Num[22],len=0;
struct HASHMAP{
int cnt;
int h[MOD+10],next[maxn];
LL st[maxn];
int ask(LL S){
int key=S%MOD;
for(int i=h[key];i;i=next[i]){
if(st[i]==S)return i;
}
++cnt;next[cnt]=h[key];h[key]=cnt;
st[cnt]=S;return cnt;
}
}H; void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
num DP(int pos,int zero,int flag,int first){
if(!pos){
num tmp;tmp.clear();
tmp.s0=zero;
return tmp;
}
if(!flag&&!first&&dp[pos][zero].s0!=-1)return dp[pos][zero];
num tmp,now;tmp.clear();
if(first){
now=DP(pos-1,0,0,1);
tmp.s0=tmp.s0+now.s0;if(tmp.s0>=mod)tmp.s0-=mod;
tmp.s1=tmp.s1+now.s1;if(tmp.s1>=mod)tmp.s1-=mod;
}else{
now=DP(pos-1,1,flag&&Num[pos]==0,0);
tmp.s0=tmp.s0+now.s0;if(tmp.s0>=mod)tmp.s0-=mod;
tmp.s1=tmp.s1+now.s1;if(tmp.s1>=mod)tmp.s1-=mod;
}
int u=flag?Num[pos]:9;
for(int i=1;i<=u;++i){
now=DP(pos-1,zero,flag&&i==u,0);
tmp.s0=tmp.s0+now.s0;if(tmp.s0>=mod)tmp.s0-=mod;
tmp.s1=tmp.s1+now.s1;if(tmp.s1>=mod)tmp.s1-=mod;
tmp.s1=tmp.s1+i*t[pos]*now.s0%mod;if(tmp.s1>=mod)tmp.s1-=mod;
}return (flag||first)?tmp:dp[pos][zero]=tmp;
}
num DFS(int pos,LL mul,int flag,int first){
if(!pos){
num tmp;tmp.clear();
if(!first&&mul==1)tmp.s0=1;
return tmp;
}
int cur=H.ask(mul);
if(!first&&!flag&&f[pos][cur].s0!=-1)return f[pos][cur];
num tmp,now;tmp.clear();
if(first){
now=DFS(pos-1,mul,0,first);
tmp.s0=tmp.s0+now.s0;if(tmp.s0>=mod)tmp.s0-=mod;
tmp.s1=tmp.s1+now.s1;if(tmp.s1>=mod)tmp.s1-=mod;
}
int u=flag?Num[pos]:9;
for(int i=1;i<=u;++i){
if(mul%i==0){
now=DFS(pos-1,mul/i,flag&&i==u,0);
tmp.s0=tmp.s0+now.s0;if(tmp.s0>=mod)tmp.s0-=mod;
tmp.s1=tmp.s1+now.s1;if(tmp.s1>=mod)tmp.s1-=mod;
tmp.s1=tmp.s1+i*t[pos]*now.s0%mod;if(tmp.s1>=mod)tmp.s1-=mod;
}
}return (flag||first)?tmp:f[pos][cur]=tmp;
}
bool judge(LL k){
for(int i=2;i<=9;++i){
while(k%i==0)k/=i;
}return k>1;
}
int main(){
scanf("%d",&T);
t[1]=1;
for(int i=2;i<=20;++i)t[i]=t[i-1]*10%mod;
memset(f,-1,sizeof(f));
memset(dp,-1,sizeof(dp));
while(T--){
scanf("%lld%lld%lld",&L,&R,&k);
if(k==0){
check(L-1);A=DP(len,0,1,1);
check(R);B=DP(len,0,1,1);
}else{
if(judge(k)){printf("0\n");continue;}
check(L-1);A=DFS(len,k,1,1);
check(R);B=DFS(len,k,1,1);
}printf("%lld\n",(B.s1-A.s1+mod)%mod);
}return 0;
}
BZOJ 3131
跟上面的题思路是一样的,先预处理出所有合法的k
设s[i]表示第i个合法的k在[1,n]中有多少乘积为k的数,做数位DP
之后用优先队列实现多路归并即可,循环K次即可
注意放进队列里的时候不要取模
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std; typedef long long LL;
const int mod=1e9+7;
const int maxn=200010;
const int MOD=1333331;
LL n;
LL f[22][20010];
LL s[20010];
int Num[22],len,k;
struct pos{
int a,b;
LL num;
pos(int a=0,int b=0,LL num=0):a(a),b(b),num(num){}
bool operator <(const pos &A)const{
return num<A.num;
}
};
priority_queue<pos>Q;
bool cmp(const LL &A,const LL &B){return A>B;}
struct HASHMAP{
int cnt;
int h[MOD+10],next[maxn];
LL st[maxn];
void insert(LL S){
int key=S%MOD;
for(int i=h[key];i;i=next[i]){
if(st[i]==S)return;
}
++cnt;next[cnt]=h[key];h[key]=cnt;
st[cnt]=S;return;
}
int ask(LL S){
int key=S%MOD;
for(int i=h[key];i;i=next[i]){
if(st[i]==S)return i;
}return 0;
}
}H;
void check(LL n){
memset(Num,0,sizeof(Num));len=0;
if(!n){Num[++len]=0;return;}
while(n)Num[++len]=n%10,n/=10;
}
LL DFS(int pos,LL mul,int flag,int first){
if(!pos){
if(!first&&mul==1)return 1;
return 0;
}
int cur=H.ask(mul);
if(!flag&&!first&&f[pos][cur]!=-1)return f[pos][cur];
LL tmp=0;
if(first)tmp=tmp+DFS(pos-1,mul,0,1);
int u=flag?Num[pos]:9;
for(int i=1;i<=u;++i){
if(mul%i==0)tmp=tmp+DFS(pos-1,mul/i,flag&&i==u,0);
}return (flag||first)?tmp:f[pos][cur]=tmp;
}
LL mul(LL a,LL b){
LL s=0;
while(b){
if(b&1)s=(s+a)%mod;
a=(a<<1)%mod;b>>=1;
}return s;
} int main(){
scanf("%lld%d",&n,&k);
check(n);memset(f,-1,sizeof(f));
for(int i=1;i<=9;++i)H.insert(i);
for(int i=2;i<=len;++i){
int now=H.cnt;
for(int k=1;k<=now;++k){
LL S=H.st[k];
for(int j=1;j<=9;++j)H.insert(S*j);
}
}
for(int i=1;i<=H.cnt;++i){
LL S=H.st[i];
if(S>n)continue;
s[i]=DFS(len,S,1,1);
}
sort(s+1,s+H.cnt+1,cmp);
for(int i=1;i<=H.cnt;++i){
Q.push(pos(i,1,s[i]*s[1]));
}
LL ans=0;
for(int i=1;i<=k;++i){
if(Q.empty())break;
pos tmp=Q.top();Q.pop();
ans=ans+tmp.num%mod;
if(ans>=mod)ans-=mod;
if(tmp.b+1<=H.cnt){
Q.push(pos(tmp.a,tmp.b+1,s[tmp.a]*s[tmp.b+1]));
}
}printf("%lld\n",ans);
return 0;
}
留下两道题目当做最后复习:一道是BZOJ的大新闻,一道是CF的题
关于今天换的模板的一些总结:
1、首先对于flag=1的时候我们不需要记忆化,因为flag=1的状态只会转移到一个唯一的状态
2、使用这个模板要在DFS的过程中判掉几乎所有的限制,然后由于记录的只是flag=0的时候的状态,所以与L和R无关
每次DP不需要清空
3、有关于前导零的处理:我是有三种方法的:
第一种:当前导零对答案无影响时,补全前导零直接DP
第二种:先DP预处理,之后查询的时候先加上<len的,再去记忆化解决=len的
第三种: 增加传参first,表示当前是否是首位来判断对于状态是否有贡献,至于是否对first进行记忆化要根据题目来定
通常情况下不需要对first记忆化
4、有关于一些坑点:
数位DP一个最需要注意的问题是要注意整数溢出
其次要考虑一些边界情况,譬如当L=0的时候,如果要Solve(R)-Solve(L-1)就挂掉了
之后考虑题目的性质是否可减,通常情况下是可减的,我们转化成Solve(R)-Solve(L-1)
5、关于模板的一些细节:
注意在判断是否已经记忆化之前要判断flag
注意记忆化的时候要判断flag
注意每次循环的上界要判断flag
6、对于状态,数位DP的状态一般很好想
但是精简的状态可能会要动些脑子,一定要考虑我们到底需要记录什么
另外数位DP写的时候一定要细心,考虑所有边界,争取一遍写对
然后考试的时候数位DP一定要拍,错的话要耐心去想去改
未完待更(以后要做一些更好的数位DP,做一做论文题)
八月份!三个月!Fighting!
My_Plan part1 小结的更多相关文章
- 20181117-python第二章学习小结-part1
什么是二进制,十进制如何转化成二进制. 在python上可使用简单的函数进行转化,bin() 数据量的基本关系: 1bit 就是0/1的一个单位 1bytes = 8bit #1个字节,就是一 ...
- 20181115 python-第一章学习小结part1
知识点回顾: 什么是编程: 写代码,让计算机执行任务 编程语言的分类与特性: 1.机器语言,即二进制语言,最帖近于机器底层,可以由计算机直接执行,故速度最快,但不适合开发. 2.汇编语言,直接将二进制 ...
- [Blog] Part1: 技术札记-写个创站小结吧
创站绝对是一个大坑 我当初真有勇气.. 嗯 这个站主要就是 Github+Jekyll+markdown 基本上还是现在能用的比较习惯的模式 基本流程概述 域名 -> 修改DNS -> g ...
- 【Hadoop】HIVE 小结概览
一.HIVE概览小结 二.HIVE安装 Hive只在一个节点上安装即可 .上传tar包 .解压 tar -zxvf hive-.tar.gz -C /cloud/ .配置mysql metastore ...
- 小课堂Week12 Clean Code Part1
小课堂Week12 Clean Code Part1 今天的主题是函数,让我们看一个函数,找一找其中的"不整洁". 我们也根据这段代码,讨论下对于整洁代码的两个重要原则. publ ...
- 小课堂Week8 例外处理设计的逆袭Part1
小课堂Week8 例外处理设计的逆袭Part1 今天和大家讲一本书,书名是<例外处理设计的逆袭>. 为什么想讲这本书,是因为,例外处理在程序代码中到处存在,但是这些到底该如何写好,总觉得有 ...
- 使用Unity创建塔防游戏(Part1)
How to Create a Tower Defense Game in Unity - Part1 原文作者:Barbara Reichart 文章原译:http://www.cnblogs.co ...
- 软工作业-----Alpha版本第一周小结
软工作业-----Alpha版本第一周小结 Part1.第一周周计划记录 姓名 学号 周前计划安排 每周工作记录 自我打分 yrz(队长) 1417 1.进行任务分析 2.任务分配 ...
- 第七章:网络优化与正则化(Part1)
任何数学技巧都不能弥补信息的缺失. --科尼利厄斯·兰佐斯(Cornelius Lanczos) 匈牙利数学家.物理学家 文章相关 1 第七章:网络优化与正则化(Part1) 2 第七章:网络优化与正 ...
随机推荐
- css解决IE6、Chrome、ff 浏览器position:fixed;和闪动问题
首先说下开发模式,刚刚开始接触,基本沿用web端开发方式,目前开发模式上没有找到的适合的,现在基本这样:1,电脑nginx配置服务器3,电脑和手机连同一个局域网,2,android root 和 ip ...
- Mod 与 RequireJS/SeaJS 的那些事
本文的目的是为了能大让家更好的认识 Mod,之所以引入 RequireJS/SeaJS 的对比主要是应大家要求更清晰的对比应用场景,并不是为了比较出孰胜孰劣,RequireJS 和 SeaJS 都是模 ...
- MySQL 简洁连接数据库方式
OS : CentOS 6.3 DB : 5.5.14 MySQL连接数据库的方式很多: 1.[root@db01 bin]# ./mysql -uroot -p 2.[root@db01 ...
- YARN环境搭建 之 一:CentOS7.0系统配置
一.我缘何选择CentOS7.0 14年7月7日17:39:42发布了CentOS 7.0.1406正式版,我曾使用过多款Linux,对于Hadoop2.X/YARN的环境配置缘何选择CentOS7. ...
- 总线(BUS)和总线操作
1.什么是总线? 答:总线是运算部件之间数据流通的公共通道. 2.总线的作用? 答:提高专用信号处理逻辑电路的运算能力和速度. 3.总线与部件之间是怎么连接的? 答:各运算部件和数据寄存器组是通过带控 ...
- 20145120 《Java程序设计》第1周学习总结
20145120 <Java程序设计>第1周学习总结 教材学习内容总结 刚刚开始学习java,感觉还十分陌生,在第一周的学习中,我知道了java的历史,JVM.JRE和JDK是什么等各种知 ...
- OC面向对象继承关系和组合关系笔记
继承关系是描述类和类之间的关系,两个类分别称为子类和父类,子类继承了父类,子类就拥有了父类的属性和方法: 继承的关系特点描述出来就是:** “是” ** (例如:学生类 是 人类) 组合关系描述的语 ...
- WPF解析PPT为图片
偶遇需要解析 PPT为单张图片 其中,对于包含动画的PPT页,分别对动画最后效果进行截取,即每个连续动画截取 (动画N个)N+1(原图)张 http://git.oschina.net/jiailiu ...
- MVC的Ajax的异步请求
MVC的Ajax的异步请求 在这里小写一下MVC的异步请求的一点小总结. 个人认为是有两种的,一种就是跟webform一样的,依旧是使用jQuery的$.get()方法,只是请求地址不同,webfor ...
- 开源 P2P 直播 视频会议
转自:http://blog.csdn.net/pkueecser/article/details/8223074 一个P2P点播直播开源项目:P2PCenter(我转过来的时候发现已经都打不开了.. ...