• 2018牛客网暑假ACM多校训练赛(第四场)C Chiaki Sequence Reloaded (组合+计数) 或 数位dp


    原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round4-C.html

    题目传送门 - https://www.nowcoder.com/acm/contest/142/C

    题意

      定义 

    $$a_n=egin{cases}0& ext{$(n=1)$}\ a_{leftlfloorfrac n2 ight floor}+(-1)^{frac{n(n+1)}2}& ext{$(n>1)$}end{cases}$$

      现在 有 $T$ 组询问,每组询问给定一个 $n$ 让你求 

    $$sum_{i=1}^{n}|a_i|$$

      $T=10^5,nleq 10^{18}$

    题解

      AC 之后查了一下别人的代码,发现居然有一份代码和我做法类似。Orz

      标算是 数位dp ,不用动什么脑子,码出来就可以了。由于博主十分的,不想写数位dp,于是瞎bb了个简单的做法,没想到真的能 AC 。顺手卡到了代码第一短和跑的第一快。

      首先,我们考虑如何得到任意 $x$ 的 $a_x$ 。不要直接代公式,我们来找找规律。我们考虑把原数写成二进制,个位为第一位。那么,假设这个数在二进制形式下有 $t$ 位,那么对于每两个连续的数位,如果他们相等,则对 $a_x$ 的贡献为 $1$ ,否则为 $-1$ 。这个很好证明的,不多说。

      预处理一下 $ans_{i,j}$ 表示后面还有 $j$ 位没有确定,而前面对答案的贡献为 $i$ 时,后面所有填法所得到的 $x$ 的 $|a_x|$ 之和。

      我们考虑枚举一下后面 $j$ 位有几位与其前一位不同。则对于每一个枚举到的值,设为 $k$ ,则可以在 $j$ 位中选择 $k$ 个让它与它的前一位相同,得到的 $a_x=i+2k-j$ ,方案总数为 $inom{j}{k}$ ,对 $ans_{i,j}$ 的贡献为 $inom{j}{k} imes |a_x|$ 。所以我们可以 $O(log^3 maxn)$ 预处理这个东西。

      对于每一个 $n$ ,把它搞成二进制形式,考虑分三种情况求答案。

      第一种情况:统计的数的位数比 $n$ 小(在二进制下),则答案贡献为 $sum_{i=1}^{d-1} ans_{0,i-1}$ 。

      第二种情况:$i$ 从高到低位枚举,当前统计的数从第 $i$ 位开始比 $n$ 小了,设 $tot$ 为比 $i$ 位高的数位以及 $i$ 本身对 $a_x$ 的贡献,则当前 $i$ 对答案的贡献为 $ans_{tot,i-1}$ 。

      第三种情况:$x=n$ ,直接把 $|a_x|$ 加到答案里就可以了。

      时间复杂度 $O(log^3 n+ T log n)$ 。

    代码

    最短和最快!

    截止 2018-07-28  23:10 最快代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return x;
    }
    const int N=140,_0=64,mod=1e9+7;
    int C[N/2][N/2],ans[N][N/2],T,d[N],t;
    LL n;
    void del(int &x){
    	if (x>=mod)
    		x-=mod;
    }
    int main(){
    	for (int i=0;i<N/2;i++)
    		C[i][0]=1;
    	for (int i=1;i<N/2;i++)
    		for (int j=1;j<=i;j++)
    			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    	for (int i=-64;i<=64;i++)
    		for (int j=0;j<=64;j++)
    			for (int k=0;k<=j;k++)
    				ans[i+_0][j]=(1LL*C[j][k]*abs(i+2*k-j)+ans[i+_0][j])%mod;
    	T=read();
    	while (T--){
    		for (n=read(),t=0;n;d[++t]=n&1,n>>=1);
    		int res=0,tot=0;
    		for (int i=t-1;i>=1;i--){
    			int k=(d[i]==d[i+1])?1:-1;
    				del(res+=ans[0+_0][i-1]);
    			if (d[i]==1)
    				del(res+=ans[tot-k+_0][i-1]);
    			tot+=k;
    		}
    		del(res+=abs(tot));
    		printf("%d
    ",res);
    	}
    	return 0;
    }
    

    截止 2018-07-28  23:18 最短代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=150,_0=74,mod=1e9+7;
    int C[N][N],ans[N][N],T,d[N],t;
    LL n;
    int main(){
        for (int i=0;i<N;i++)
            C[i][0]=1;
        for (int i=1;i<N;i++)
            for (int j=1;j<=i;j++)
                C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        for (int i=-64;i<=64;i++)
            for (int j=0;j<=64;j++)
                for (int k=0;k<=j;k++)
                    ans[i+_0][j]=(1LL*C[j][k]*abs(i+2*k-j)+ans[i+_0][j])%mod;
        scanf("%d",&T);
        while (T--){
            scanf("%lld",&n);
            for (t=0;n;d[++t]=n&1,n>>=1);
            int res=0,tot=0;
            for (int i=t-1;i>=1;i--){
                int k=(d[i]==d[i+1])?1:-1;
                res=(res+ans[0+_0][i-1])%mod;
                if (d[i]==1)
                    res=(res+ans[tot-k+_0][i-1])%mod;
                tot+=k;
            }
            printf("%d
    ",(res+abs(tot))%mod);
        }
        return 0;
    }
    

      

  • 相关阅读:
    23种设计模式总篇
    23种设计模式之抽象工厂
    23种设计模式之原型模式
    23种设计模式之适配器模式
    23种设计模式之工厂模式
    23种设计模式之模板方法
    Cloudera Manager 5和CDH5离线安装
    ArrayList vs. LinkedList vs. Vector
    在Java中怎样把数组转换为ArrayList?
    两个有序数组的中位数 【算法】
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round4-C.html
Copyright © 2020-2023  润新知