TJOI2018 party
小豆参加了 NOI 的游园会,会场上每完成一个项目就会获得一个奖章,奖章只会是 N, O, I 的字样。
在会场上他收集到了 (K) 个奖章组成的串。兑奖规则是奖章串和兑奖串的最长公共子序列长度为小豆最后奖励的等级。
现在已知兑奖串长度为 (N) ,并且在兑奖串上不会出现连续三个奖章为 NOI
,即奖章中不会出现子串 NOI
。
现在小豆想知道各个奖励等级会对应多少个不同的合法兑奖串。
对于 (100\%) 的数据, (N leq 1000 , K leq 15) 。
BZOJ3684 Hero meet devil
这原题搬的……无语了。
LCS的DP相信大家都很熟练了。设(f[i][j])表示(T)的前(i)位和(S)的前(j)位的LCS是多少。
设(F[i][S])表示考虑(T)的前(i)位,DP数组(f[i])是(S)的方案数。
注意到(f[i][j − 1] ≤ f[i][j] ≤ f[i][j − 1] + 1),所以(S)中只要记录相邻两项的差值,不是(0)就是(1)。
转移枚举字符是什么,可以用预处理转移做到(O(1))。
时间复杂度是(O(M × 2^N))。
至于不能出现NOI
……这就是个傻逼KMP限制。
CO char NOI[4]="NOI";
char str[16];
int go[1<<15][3];
int F[2][1<<15][3];
int ans[16];
int main(){
int n=read<int>(),K=read<int>();
scanf("%s",str+1);
function<int(int,int)> calc=[&](int s,int c){
vector<int> lst(K+1),cur(K+1);
for(int i=1;i<=K;++i) lst[i]=lst[i-1]+(s>>(i-1)&1);
for(int i=1;i<=K;++i){
if(str[i]==NOI[c]) cur[i]=lst[i-1]+1;
else cur[i]=max(lst[i],cur[i-1]);
}
int ans=0;
for(int i=1;i<=K;++i) ans|=(cur[i]-cur[i-1])<<(i-1);
return ans;
};
for(int s=0;s<1<<K;++s)
for(int c=0;c<3;++c) go[s][c]=calc(s,c);
F[0][0][0]=1;
for(int i=1;i<=n;++i){
memset(F[i&1],0,sizeof F[i&1]);
for(int s=0;s<1<<K;++s)
for(int x=0;x<3;++x)if(F[(i-1)&1][s][x])
for(int c=0;c<3;++c){
int y;
if(c==0) y=1;
else y=c==x?x+1:0;
if(y==3) continue;
chkadd(F[i&1][go[s][c]][y],F[(i-1)&1][s][x]);
}
}
for(int s=0;s<1<<K;++s)for(int x=0;x<3;++x)
chkadd(ans[popcount(s)],F[n&1][s][x]);
for(int i=0;i<=K;++i) printf("%d
",ans[i]);
return 0;
}
HDU4352 XHXJ's LIS
设(f(i))表示把(i)看成字符串后最长上升子序列的长度,给定(l, r, k),求满足(f(i) = k~(i ∈ [l, r]))的(i)的个数。
(1 ≤ l ≤ r ≤ 10^{18},1 ≤ k ≤ 10)。
题解
因为只有(0sim 9)这(10)个数字,所以最长上升子序列长度(leq 10)。考虑求LIS的(O(nlog n))做法,那么我们状压记录(0sim 9)中哪些数字出现在了LIS的贪心数组中。
(dp[pos][1<<10][k])表示:(pos)位,用二进制的每一位表示最长上升子序列都出现了那些数,最长上升子序列的长度为(K);对新增加的一个数字,我们判断它前面一个数字是否比他小(小就替换),没有则将其加入;
int K,bit[20];
int64 dp[11][20][1<<10];
int trans(int s,int x){
for(int i=x;i<=9;++i)if(s>>i&1)
return (s^1<<i)|1<<x;
return s|1<<x;
}
int64 dfs(int p,int s,bool lim,bool emp){
if(!p) return popcount(s)==K;
if(!lim and dp[K][p][s]!=-1) return dp[K][p][s];
int up=lim?bit[p]:9;
int64 ans=0;
for(int i=0;i<=up;++i)
ans+=dfs(p-1,emp and i==0?0:trans(s,i),lim and i==up,emp and i==0);
if(!lim) dp[K][p][s]=ans;
return ans;
}
int64 solve(int64 n){
int l=0;
for(;n;n/=10) bit[++l]=n%10;
return dfs(l,0,1,1);
}
int main(){
int T=read<int>();
memset(dp,-1,sizeof dp);
for(int i=1;i<=T;++i){
int64 a=read<int64>(),b=read<int64>();
read(K);
printf("Case #%d: %lld
",i,solve(b)-solve(a-1));
}
return 0;
}