【GDOI2016模拟4.23】无界单词
2016-04-26 19:26
309 查看
Description
学过kmp吗?一个只由a和b组成的字符串S,如果next[|S|]=0,那么这个单词就是无界的,否则就是有界的。
给出n和k,求长度为n的无界单词有多少个,和其中字典序第k小的是什么。
多组询问。
Type<=50,n<=64
Solution
很考验思维的一道题。一般人(我)看到就想到鬼畜数论,结果正解是dp(也不算吧)
首先处理第一问。
好像很难做正难则反。
设FiFi表示长度为i的无界单词的数量,那么,我们只需要用2i2^i减去有界单词的数量。
一个很明显的性质,如果一个有界单词最小的前后缀相等长度为j,那么它长度为j的前缀一定是无界单词。
另一个性质,j<=i/2。
那么我们可以通过枚举j,把这一段复制到后面,剩下的任选,那么Fi=2i−∑j=1i2Fj∗2i−2∗jFi=2^i-\sum_{j=1}^{i\over 2}Fj*2^{i-2*j}
并且这样是不会算重的。
那么第二问呢?
我们一位一位枚举,先填a,如果此时剩余的无界单词数量< k,那么这一位就必须填b,并且k要减去剩余的数量。如何计算剩余的数量?
还是使用上面的思路。
假设前Len位已经确定,那么我们可以分类讨论。
1.[b]len>=i[/b]
这时的字符串已经是确定的,那么只需要用kmp(暴力)就好了。
2.[b]len<=j[/b]
这时我们复制是不会有重复的,也不会有任何影响,就像第一问的方法一样做就好了。
3.[b]len>j&&len<=i-j[/b]
差不多,但是中间本来任选的地方也有一些是确定的,只需要改变任选的方案数成2i−j−len2^{i-j-len}就好了。
4.[b]len>i-j[/b]
这样的话中间就没有任选的了,并且我们复制的时候会和原来已经确定的部分重叠,hash判断是否可行。
Code
#include<cstdio> #include<cstring> #include<algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) #define N 65 using namespace std; typedef long long ll; ll f ,mi ,h ,cnt,k; int n,ty,next ,j,ans ,len; int hash(int x) { int y=len-x; return h[x]==h[len]-h[y]*mi[x]; } ll calc(int len) { fo(i,1,len) f[i]=!next[i]; fo(i,len+1,n) { f[i]=mi[i-len]; fo(j,1,i/2) { if (j>=len) cnt=mi[i-2*j]; if (len>j&&len<=i-j) cnt=mi[i-j-len]; if (len>i-j) cnt=hash(len-i+j); f[i]-=f[j]*cnt; } } return f ; } void push(int c) { ans[++len]=c;h[len]=h[len-1]*2+c; if (len==1) return; while (j&&ans[j+1]!=ans[len]) j=next[j]; next[len]=j+=(ans[j+1]==ans[len]); } ll work() { printf("%lld\n",calc(0));j=len=0; memset(next,0,sizeof(next)); fo(i,1,n) { int l=j;push(0); ll cnt=calc(len); if (cnt<k) len--,j=l, push(1),k-=cnt; } fo(i,1,n) printf("%c",ans[i]+'a'); printf("\n"); } int main() { freopen("word.in","r",stdin); freopen("word.out","w",stdout); mi[0]=1;fo(i,1,64) mi[i]=mi[i-1]*2; for(scanf("%d",&ty);ty;ty--) scanf("%d%lld",&n,&k),work(); }
相关文章推荐
- C++实验4
- Python 内置函数 range的使用
- CSS3动画工具
- 递归算法 笔记
- MATLAB——scatter的简单应用
- java中四种操作(dom、sax、jdom、dom4j)xml方式详解与比较
- 关于工作流后台流程并行程序的有用信息
- error: zlib.h:no such file or directory
- 【Nginx】下载,请求限流限速,根据URL参数限速
- Android技术知识分享第一弹——SDK中常用命令
- 【Nginx】下载,请求限速,根据URL参数限速
- cookie、session、sessionid 与jsessionid
- hdu2795Billboard
- Git学习3:理解工作区和暂存区
- 微信网页获取openId
- 【linux网络】ip_rcv()函数
- 手残星人的环境搭建之路——ubuntu必要环境配置
- Android开发中通过源码彻底理解ListView工作原理
- uWSGI+Nginx+Django安装和配置
- Mac系统如何配置adb