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\)

posted @ 2021-03-18 22:16  ファイナル  阅读(53)  评论(0)    收藏  举报