Description
Soaring 决定在其最强的运动项目足球比赛中进行推广使用。他的革命性设计将被用在下一轮奥运会预选赛中。他目前正在说服足协董事会。球员替换牌在足球比赛中用以显示被换下球员的球衣号码,Soaring 设计的替换牌有 M 个 5 段显示器,从左到右依次排开。Soaring 在游说时,先把替换牌设置成数 X,同时,然后接下来每秒进行一次操作。操作有两种:
Input
Output
Sample Input
input1:
1 2 1 5
input2:
1 3 3 8
input3:
1 4 2 4
Sample Output
output1:
Data Constraint
子任务 | 限制 | 分值 |
---|---|---|
1 | m=1,1<=n<=12 | 6 |
2 | m=1,1<=n<=10^15 | 15 |
3 | m=2,1<=n<=1500,k=n | 12 |
4 | m=2,1<=n<=10^15,1<=k<=15 | 12 |
5 | m=2,1<=n<=10^15,1<=k<=1500 | 15 |
6 | m=2,1<=n<=10^15 | 4 |
Solution
先将数字转化一下,对于每一个数字可以用一个5位二进制数表示用不用那一段。
而一个二位数就可以表示为一个10位二进制数。
先考虑暴力dp转移。
设f[ i ][ S ]表示走了i步,当前的数的状态为S的方案数。
则初始f[ 0 ][ state[x] ]=1。
转移只需要枚举一个 j ,表示翻转状态的第j位,
每当i为k的倍数时再将不合法的状态清空即可,f[ i ][ S ]=0 (S不能被表示为某一个数的状态 ) 。
可以用矩阵乘法优化。
设 f[ S ]表示走了n步状态为S的方案数,转移矩阵即为 g[ i ][ i ^ (1<<j ) ]=1。
答案就是 。
但是考虑到有k的限制,不妨拆开若干部分讨论。
先走k步,再k步k步走n/k次,最后再走n%k步。
但是我们发现转移矩阵是2^10 *2^10的,时间复杂度达到了(2^10)^3不能接受。
考虑将转移矩阵缩小。
注意到前k步的转移矩阵没有限制,可以随便走。
因此对于走的任意的从x走到y的状态,相同的x^y的矩阵g[ x ][ y ]的值一定是相同的,因为状态改变量相同即步数相同。
那么我们将矩阵含义变成 g[ x ] 表示 该变量为 x 时的方案数。
考虑之前的转移:
现在变成:
变形,得:
令x= i^k, y= k^j,有:
也就是说我们只需要枚举一个x和y,就可以转移到 G[x^y]了
至此,我们成功地将复杂度降低为(2^10)^2级别。
其实上式就是异或卷积的形式。
处理完这个之后我们发现因为k步之后必为一个有效的数字,因此我们可以只记录下数字之间的转移,这样就可以避免再将不合法状态清空了。
于是设a[ i ][ j ]表示刚好经过k步之后从数字 i 变成 数字 j 的方案数 (可以为一位数或者两位数)。
接下来我们就可以用标准的矩阵乘法转移a了,这里我们转移n/k次,表示k步k步走,同时也保证了每k步之后必为有效的状态。
再设f[ i ]表示 从初始数字x走到数字 i 走n步的方案数。
那么这里 。
最后就是处理n%k的余下部分,这一部分同样没有限制,因此和第一部分一样,最后再将处理出来的 f 和 A 乘起来就是答案了。
这题特别要注意的是:
在处理一位数的时候,数字只有一位,状态只有2^5。
但是在处理两位数时,所有数字都是两位,因此一位数的状态要加上前导0的状态!!!
这个地方调了我1h+。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll unsigned long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof a)
#define N 1050
#define M 1000000007
using namespace std;
ll n,K,m,st,now,s[102],g[N],t[N],h[N],f[102],a[102][102],c[102][102];
I num[10][5]={{0,1,0,1,0},{0,1,0,0,0},{1,0,0,1,0},{1,1,1,0,0},{0,1,0,0,1},
{1,0,1,0,1},{0,0,1,1,0},{1,1,0,0,0},{1,0,1,1,1},{1,1,1,0,1}};
I p[3]={0,4,9},q[3]={0,31,1023},l[3]={0,9,99};
I R(ll &x){
x=0;char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
}
void tim(ll a[N],ll b[N]){
F(i,0,q[m]) F(j,0,q[m]) t[i^j]=(t[i^j]+a[i]*b[j]%M)%M;
F(i,0,q[m]) a[i]=t[i],t[i]=0;
}
void work(ll now){
mem(g,0),mem(h,0);
F(i,0,p[m]) g[1<<i]=h[1<<i]=1;
while(now){
if(now&1) tim(g,h);
tim(h,h);
now>>=1;
}
mem(a,0);
F(i,0,l[m]) F(j,0,l[m]) a[i][j]=g[s[i]^s[j]]%M;
}
void cg(){
F(i,0,l[m]) F(j,0,l[m]) t[i]=(t[i]+f[j]*a[j][i]%M)%M;
F(i,0,l[m]) f[i]=t[i],t[i]=0;
}
I main(){
freopen("display.in","r",stdin);
freopen("display.out","w",stdout);
R(m),R(n),R(K),R(st);
F(i,0,9){
F(j,0,4) s[i]=s[i]*2+num[i][j];
}
if(m==2){
F(i,10,l[m]){
I x=i/10,y=i%10;
s[i]=(s[x]<<5)+s[y];
}
F(i,1,9) s[i]=(s[0]<<5)+s[i];
s[0]=(s[0]<<5)+s[0];
}
work(K-1);
f[st]=1;
now=n/K;
while(now){
if(now&1) cg();
mem(c,0);
F(i,0,l[m]) F(j,0,l[m]) F(k,0,l[m]) c[i][j]=(c[i][j]+a[i][k]*a[k][j]%M)%M;
F(i,0,l[m]) F(j,0,l[m]) a[i][j]=c[i][j];
now>>=1;
}
if(n%K){work(n%K-1),cg();}
F(i,0,9+(m==2)*90) printf("%lld\n",f[i]);
return 0;
}