bzoj 1581: [Usaco2009 Hol]Transmission Delay 传输谍延时

Description

    约翰在屋顶上唱歌,以此来与奶牛们交流.但是奶牛们的听力很奇怪,她们只能听到约翰的歌声变成0和1构成的信息串时的样子.    约翰的声音里有N(1≤N≤2000)个0或1,奶牛听到的也是N个,而且0和1的数量不会变化,但是一部分0或1可能偏离原来的位置,这就是约翰的歌声在传输时发生的“传输延迟”现象.0或1的偏离距离不会超过D(O≤D<N),也就是说某一个码的原本位置和现在的位置之差的绝对值不大于D.
    比如,对于0110,D=1,传输延迟发生后可能出现0101,0110,1001,1010这四种串.
    给出约翰歌声的01串形式和一个整数K(1≤K≤108),请计算传输延迟发生后一共有多少种可能的01串,以及其中第K大的串是什么.

Input

第一行输入 n , d , k 第二行输入那个N位的数字,用二进制表示

Output

分别输出受到整数的种数(将结果Mod 100,000,000),和这些整数第K小的那个

Sample Input

4 1 3
0110

Sample Output

4
1001

HINT

 

1<=n<=2000 0<=k

 

Source

这题是个dp。

定义方程f[i][j]表示从n~i,用了j个0的方案数。要n~i倒着处理是为之后的第k大做帮助。

第一问就直接dp,我们记录下p0[i],p1[i]分别表示从左往右第i个0的位置,第i个1的位置。转移的时候根据当前位放0还是放1,且是否满足距离小于等于d,计算即可。

然后对于第二问,我们这么做:

  • 如果当前位置放0的方案数>=k,当前位就放0。

  • 如果不能放0,就放1,并且k减去放0的方案数。

以上所有说的可以放0或放1都是建立在题目要求的前提下的,即距离不超过d

然后我们有一个问题了:如果方案数超过k的超过太多了怎么办?按照题意来说我们应该是要%10^8的,但是由于要求后面的第k大需要用到这个方案数,如果我们直接%10^8,就会导致结果不正确了。

对于这个问题我们再开一个g[i][j]也记录方案数,如果g[i][j]>10^8了就把它设成10^8+1,然后之后算第k大的时候就用g[i][j]当做方案数,这样就没事了。

下面放代码:

 

 1 #include<bits/stdc++.h>
 2 using namespace std; 
 3 int const N=2000+3;  
 4 int const mod=1e8;  
 5 int f[N][N],g[N][N],s0,s1,p0[N],p1[N],n,d,k;  
 6 char s[N];  
 7 int main(){
 8     scanf("%d%d%d",&n,&d,&k);  
 9     scanf("%s",s+1);  
10     for(int i=1;i<=n;i++)  
11         if(s[i]==48) p0[++s0]=i;  
12         else  p1[++s1]=i;
13     f[n+1][0]=g[n+1][0]=1;  
14     for(int i=n;i>=1;i--)  
15         for(int j=0;j<=s0;j++) {
16             if(j && abs(p0[s0-j+1]-i)<=d)  
17             {
18                 f[i][j]+=f[i+1][j-1];  
19                 f[i][j]%=mod; 
20                 g[i][j]+=g[i+1][j-1];  
21                 if(g[i][j]>mod) g[i][j]=mod+1;  
22             }
23             if(n-i+1-j<=s1 && abs(p1[s1-(n-i+1-j)+1]-i)<=d) {
24                 f[i][j]+=f[i+1][j];  
25                 f[i][j]%=mod; 
26                 g[i][j]+=g[i+1][j]; 
27                 if(g[i][j]>mod) g[i][j]=mod+1; 
28             }
29         }
30     printf("%d\n",f[1][s0]);
31     int num0=s0,num1=s1;   
32     for(int i=1;i<=n;i++) {
33         if(num0 && abs(p0[s0-num0+1]-i)<=d)
34         {
35             if(g[i+1][num0-1]>=k)  putchar('0'),num0--; 
36             else putchar('1'),k-=g[i+1][num0-1],num1--; 
37         }
38         else num1--,putchar('1');    
39     }
40     return 0; 
41 }
View Code

 

posted @ 2019-04-30 17:19  zjxxcn  阅读(321)  评论(0编辑  收藏  举报