CF908G New Year and Original Order
CF908G New Year and Original Order
开始看到题时一头雾水:什么鬼东西,还带排序的
后来才从\(\tt tly\)大佬那里学到了正解
首先我们可以先将\(S\)函数的值拆分
\(S(353535)=333555=111111+111111+111111+111+111\)
于是我们可以发现,令\(s_i\)表示每一位中等于\(i\)的个数和的后缀和,
例如\(s_5=s_4=3,s_3=s_2=s_1=s_0=6\)
则\(S(x)=\frac{1}{9}\sum_{i=1}^910^{s_i}-1\)
所以我们可以对每一位分开来算贡献
但是我们考虑到\(X\)的限制,所以我们先枚举后\(m\)位不等于\(X\)的后\(m\)位,前面的位相等,再枚举第\(m\)位上的数字
但是在这里,我们只知道前面位和第\(m\)位上的数字,所以我们重新定义\(s_i\)为已知的位中等于\(i\)的后缀和
于是数字\(i\)对于当前情况的贡献为\(\frac{1}{9}\sum_{p=0}^m{m\choose p}i^{m-p}(10-i)^p(10^{s_i+p}-1)\\ \)
意义为枚举大于等于\(i\)的个数\(p\),\(\frac{10^{s_i+p}-1}{9}\)则表示那一段\(1\)
这样我们就可以得到一个时间复杂度为\(O(k^2N^2)(N=\text{len(n)}\leq 700)\)的算法
#include<bits/stdc++.h>
using namespace std;
# define ll long long
# define read read1<ll>()
# define Type template<typename T>
Type T read1(){
T t=0;char k;bool vis=0;
do (k=getchar())=='-'&&(vis=1);while('0'>k||k>'9');
while('0'<=k&&k<='9')t=(t<<3)+(t<<1)+(k^'0'),k=getchar();
return vis?-t:t;
}
# define fre(k) freopen(k".in","r",stdin);freopen(k".out","w",stdout)
# define mod 1000000007
# define inv9 111111112ll
char str[705];
int s,h[10];
ll P[705][12],C[705][705];
int main(){
scanf("%s",str+1);
s=strlen(str+1);reverse(str+1,str+s+1);
for(int i=0;i<12;++i)P[0][i]=1;
for(int i=1;i<=s;++i)
for(int j=1;j<12;++j)
P[i][j]=P[i-1][j]*j%mod;
for(int i=0;i<=s;++i){
C[i][0]=C[i][i]=1;
for(int j=1;j<i;++j)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
ll ans=0;
for(int i=s;i;--i){
str[i]-='0';ll k=0;
if(i!=s)++h[str[i+1]];
for(int j=0;j<str[i];++j){
++h[j];int o=0;
for(int l=10;--l;){
o+=h[l];//m=i-1;
for(int p=0;p<i;++p)
k=(k+C[i-1][p]*P[i-p-1][l]%mod*P[p][10-l]%mod*(P[o+p][10]-1))%mod;
ans=(ans+inv9*k)%mod;k=0;
}
--h[j];
}
}
sort(str+1,str+s+1);
ll sum=0;
for(int i=1;i<=s;++i)sum=(10ll*sum+str[i])%mod;
cout<<(ans+sum)%mod;
return 0;
}
虽然可以过,但我们可以进行优化。
对贡献进行二项式展开逆变换
则贡献为\(\frac{1}{9}(10^{s_i}(100-9i)^m-10^m)\)
则得到了一个\(O(k^2N)\)的算法
#include<bits/stdc++.h>
using namespace std;
# define ll long long
# define read read1<ll>()
# define Type template<typename T>
Type T read1(){
T t=0;char k;bool vis=0;
do (k=getchar())=='-'&&(vis=1);while('0'>k||k>'9');
while('0'<=k&&k<='9')t=(t<<3)+(t<<1)+(k^'0'),k=getchar();
return vis?-t:t;
}
# define fre(k) freopen(k".in","r",stdin);freopen(k".out","w",stdout)
# define mod 1000000007
# define inv9 111111112ll
char str[705];
int s,h[10];
ll P[705][12],C[705][10];
int main(){
scanf("%s",str+1);
s=strlen(str+1);reverse(str+1,str+s+1);
for(int i=0;i<12;++i)P[0][i]=1;
for(int i=0;i<10;++i)C[0][i]=1;
for(int i=1;i<=s;++i)
for(int j=1;j<12;++j)
P[i][j]=P[i-1][j]*j%mod;
for(int i=1;i<=s;++i)
for(int j=0;j<10;++j)
C[i][j]=C[i-1][j]*(100-9*j)%mod;
ll ans=0;
for(int i=s;i;--i){
str[i]-='0';ll k=0;
if(i!=s)++h[str[i+1]];
for(int j=0;j<str[i];++j){
++h[j];int o=0;
for(int l=10;--l;){
o+=h[l];
ans=(ans+P[o][10]*C[i-1][l]-P[i-1][10])%mod;
}
--h[j];
}
}ans=ans*inv9%mod;
sort(str+1,str+s+1);
ll sum=0;
for(int i=1;i<=s;++i)sum=(10ll*sum+str[i])%mod;
cout<<(ans+sum+mod)%mod;
return 0;
}
其实哪怕到了这里,依然可以进行优化。
我们发现每次枚举第\(m\)位时\(s\)的变换都不会影响小于当前枚举的数位,所以可以\(O(1)\)更新
\(\mathfrak{Talk\ is\ cheap,show\ you\ the\ code.}\)
#include<bits/stdc++.h>
using namespace std;
# define ll long long
# define read read1<ll>()
# define Type template<typename T>
Type T read1(){
T t=0;char k;bool vis=0;
do (k=getchar())=='-'&&(vis=1);while('0'>k||k>'9');
while('0'<=k&&k<='9')t=(t<<3)+(t<<1)+(k^'0'),k=getchar();
return vis?-t:t;
}
# define fre(k) freopen(k".in","r",stdin);freopen(k".out","w",stdout)
# define mod 1000000007
# define inv9 111111112ll
char str[705];
int s,h[10];
ll P[11][705],C[10][705];
int main(){
scanf("%s",str+1);
s=strlen(str+1);reverse(str+1,str+s+1);
for(int i=0;i<11;++i)P[i][0]=1;
for(int i=0;i<10;++i)C[i][0]=1;
for(int j=1;j<11;++j)
for(int i=1;i<=s;++i)
P[j][i]=P[j][i-1]*j%mod;
for(int j=0;j<10;++j)
for(int i=1;i<=s;++i)
C[j][i]=C[j][i-1]*(100-9*j)%mod;
ll ans=0;
for(int i=s;i;--i){
str[i]-='0';ll k=0;int o=0;
if(i!=s)++h[str[i+1]];
ll t=0;
for(int l=10;--l;){
o+=h[l];
t=(t+P[10][o]*C[l][i-1]-P[10][i-1])%mod;
}if(str[i])ans=(ans+t)%mod;
for(int j=1;j<str[i];++j){
t=(t+C[j][i-1]*P[10][o]*9ll)%mod;
o-=h[j];ans=(ans+t)%mod;
}
}ans=ans*inv9%mod;
sort(str+1,str+s+1);
ll sum=0;
for(int i=1;i<=s;++i)sum=(10ll*sum+str[i])%mod;
cout<<(ans+sum+mod)%mod;
return 0;
}
时间复杂度\(O(kN)\),目前全洛谷\(rk1\)
浮世苍茫,不过瞬逝幻梦
善恶爱诳,皆有定数
于命运之轮中
吞噬于黄泉之冥暗
呜呼,吾乃梦之戍人
幻恋之观者
唯于万华镜中,永世长存

浙公网安备 33010602011771号