• 【CF1601F】Two Sorts(Meet in Middle)


    题目链接

    • 定义 \(a_{1\sim n}\) 为将 \(1\sim n\) 按字典序从小到大排序后的结果,求 \((\sum_{i=1}^n(i-a_i)\ \operatorname{mod}\ 998244353)\ \operatorname{mod}\ 10^9+7\)
    • \(1\le n\le10^{12}\)

    题意转化

    这题的求和中有两种不同的取模,看起来非常麻烦。

    考虑取模的一个经典转化,即 \(x\ \operatorname{mod}\ y=x-\lfloor\frac xy\rfloor\times y\)

    所以原式可以写成:\((\sum_{i=1}^ni-a_i-\lfloor\frac{i-a_i}{998244353}\rfloor\times998244353)\ \operatorname{mod}\ 10^9+7\)

    发现 \(\sum i-a_i=0\),所以要求的就是 \(\sum_{i=1}^n\lfloor\frac{i-a_i}{998244353}\rfloor\)

    Meet in Middle

    考虑 Meet in Middle,枚举最高的 \(6\) 位,则最高 \(6\) 位为枚举值的数肯定在 \(a\) 中是连续的一段。

    对于一个长度为 \(6+l\)、较低位在所有可能的较低位中字典序排名为 \(rk\) 的数 \(y\),假设前 \(6\) 位数为 \(x\)、前 \(6\) 位小于 \(x\) 的数共有 \(ct\) 个,则它产生的贡献应该是 \(\lfloor\frac{(ct+rk)-(x10^l+y)}{998244353}\rfloor\),可以拆成只与前 \(6\) 位和 \(l\) 有关的 \(ct-x10^l\) 以及只与较低位有关的 \(rk-y\)

    形如 \(\lfloor\frac{A+B}C\rfloor\) 的式子有个经典拆分方式:\(\lfloor\frac{A+B}C\rfloor=\lfloor\frac AC\rfloor+\lfloor\frac BC\rfloor+\lfloor\frac {(A\ \operatorname{mod}\ C)+(B\ \operatorname{mod}\ C)}C\rfloor\)

    \(\lfloor\frac AC\rfloor\)\(\lfloor\frac BC\rfloor\) 分别只与 \(A,B\) 有关可以各自计算,而 \(\lfloor\frac {(A\ \operatorname{mod}\ C)+(B\ \operatorname{mod}\ C)}C\rfloor\) 在已知 \(A\) 情况下只要求有多少 \(B\ \operatorname{mod}\ C\) 大于等于 \(C-(A\ \operatorname{mod}\ C)\),可以事先拿一个 vector 存下所有 \(B\ \operatorname{mod}\ C\) 并排序,询问时 lower_bound 一下即可。

    因此先 dfs 一遍所有可能的较低位,分 \(l\) 存下 \(rk-y\) 并排序。

    然后再 dfs 一遍所有可能的最高 \(6\) 位,不足 \(6\) 位直接统计,否则根据预先存下的信息计算。

    实现起来可能要注意一些细节,感觉我的写法似乎有点麻烦。

    代码:\(O(\sqrt n\log \sqrt n)\)

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define SZ 1000000
    #define V 998244353
    #define X 1000000007
    #define LL long long
    #define Dec(x) (!x--&&(x+=X))
    using namespace std;
    LL n,nn,p;int ans,tn[7],c[7][7],s[7][7][SZ+1];
    void Init(RI x,RI l,CI k)//预处理较低位,存下所有p-x
    {
    	s[k][l][++c[k][l]]=(++p)-x;if(l==k) return;for(RI i=0;i<=9;++i) Init(x*10+i,l+1,k);
    }
    void BF(LL x,RI l)//暴力
    {
    	if(x>n) return;++p;ans=(ans+(p-x)/V-(p<x)+X)%X;for(RI i=0;i<=9;++i) BF(x*10+i,l+1);
    }
    void Solve(RI x,RI l)//枚举最高6位
    {
    	if(l^6) {++p,ans=(ans+(p-x)/V-(p<x)+X)%X;for(RI i=0;i<=9;++i) Solve(x*10+i,l+1);return;}//不足6位,直接统计
    	if(x==nn) return BF(x,0);//最高6位与n的最高6位相同,直接暴力
    	RI i,k;LL v;for(k=0,v=x;k<=6&&v<=n;++k,v*=10);--k;//求出较低位最大长度
    	for(i=0;i<=k;++i) v=p-1LL*x*tn[i],ans=(ans+1LL*(v/V-(v<0)+X)*tn[i])%X,//枚举长度,加上的数为p-x*tn[i]
    		v=(v%V+V)%V,ans=(ans+tn[i]-(lower_bound(s[k][i]+1,s[k][i]+tn[i]+1,V-v)-s[k][i])+1)%X;//求相加大于等于998244353的数的个数
    	for(i=0;i<=k;++i) p+=tn[i];//更新排名
    }
    int main()
    {
    	RI i,k,dc;for(scanf("%lld",&n),tn[0]=i=1;i<=6;++i) tn[i]=tn[i-1]*10;
    	for(k=0;k<=6;++k) for(p=0,Init(0,0,k),i=0;i<=k;++i) sort(s[k][i]+1,s[k][i]+tn[i]+1);//排序
    	if(p=0,n<1e6) for(i=1;i<=9;++i) BF(i,1);else {nn=n;W(nn>=1e6) nn/=10;for(i=1;i<=9;++i) Solve(i,1);}//n小直接暴力,否则nn记录前6位
    	return printf("%d\n",(int)(1LL*(X-ans)*V%X)),0;//答案为-ans*998244353
    }
    
  • 相关阅读:
    .net中实现运行时从字符串动态创建对象
    C# 用 VB.net 函數庫 實現全角與半角轉換
    實現.net 加載插件方式
    VS2008下載
    Lotus Notes Send EMail from VB or VBA
    用C#写vs插件中的一些Tip
    SQL2005中异常处理消息框可直接使用
    C#路径/文件/目录/I/O常见操作汇总
    利用.net反射动态调用指定程序集的中的方法
    说说今年的计划
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF1601F.html
Copyright © 2020-2023  润新知