zoukankan      html  css  js  c++  java
  • 【HDU7060】Separated Number

    题目

    题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=7060
    给定一个 (n) 位数 (x),定义把这个数划分成若干段的代价为每一段的数字之和。求把 (x) 分成不超过 (k) 段的所有方案的代价之和。
    (kleq nleq 10^6)(Qleq 5)

    思路

    下文中一个数的第 (i) 位表示从低位到高位的第 (i) 个数字。
    分别考虑每一个数字的贡献是不现实的。那么考虑最后的划分中,作为第 (i) 位出现的数字的贡献。
    因为 (x) 的第 (i) 位无论怎么划分一定会被算进来,所以我们单独拿出来考虑。剩余可能作为第 (i) 为出现的数就只有第 (i+1sim n) 位的数。
    如果 (x) 的第 (j(i<jleq n)) 位在划分中作为第 (i) 位出现了,那么第 (j-i+1) 位和最后一位后面必须划分一次,且第 (j-i+2sim j) 位后面都不能划分。那么相当于剩余的 (n-i-1) 位中可以随意划分不超过 (k-2) 次。
    (s_i) 表示 (x)(isim n) 位的和,那么作为第 (i) 为出现的数字的贡献为(不计算 (x) 的第 (i) 位的贡献)

    [s_{i+1} imes 10^{i-1}left(sum^{k-2}_{j=0}inom{n-i-1}{j} ight) ]

    再考虑 (x) 的第 (i) 位作为划分出来的数的第 (i) 位的贡献。因为第 (n) 位后面必须划分,所以相当于在剩余 (n-i) 位中可以随意划分不超过 (k-1) 次。贡献为

    [a_{i} imes 10^{i-1}left(sum^{k-1}_{j=0}inom{n-i}{j} ight) ]

    两个部分加起来就好了。
    但是这样时间复杂度是 (O(Qnk)) 的。观察到组合数的前缀和上界都是固定的,也就是相当于每次需要快速求杨辉三角的某一行的前 (k-2)(或 (k-1))个数字的和,对于 (ileq k-2) 的部分就是 (2^i),否则可以递推求。
    时间复杂度 (O(Qn))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1000010,MOD=998244353;
    int Q,n,k;
    char s[N];
    ll a[N],pw[N],pw2[N],fac[N],inv[N];
    
    ll fpow(ll x,ll k)
    {
    	ll ans=1;
    	for (;k;k>>=1,x=x*x%MOD)
    		if (k&1) ans=ans*x%MOD;
    	return ans; 
    }
    
    ll C(int n,int m)
    {
    	if (n<m) return 0;
    	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
    }
    
    int main()
    {
    	fac[0]=inv[0]=pw[0]=pw2[0]=1;
    	for (int i=1;i<N;i++)
    	{
    		fac[i]=fac[i-1]*i%MOD;
    		pw[i]=pw[i-1]*10LL%MOD;
    		pw2[i]=pw2[i-1]*2LL%MOD;
    	}
    	inv[N-1]=fpow(fac[N-1],MOD-2);
    	for (int i=N-2;i>=1;i--)
    		inv[i]=inv[i+1]*(i+1)%MOD;
    	scanf("%d",&Q);
    	while (Q--)
    	{
    		scanf("%d%s",&k,s+1);
    		n=strlen(s+1);
    		for (int i=1;i<=n;i++)
    			a[i]=(a[i-1]+s[i]-48)%MOD;
    		if (k==1)
    		{
    			ll sum=0;
    			for (int i=1;i<=n;i++)
    				sum=(10LL*sum+s[i]-48)%MOD;
    			cout<<sum<<"
    ";
    			continue;
    		}
    		ll ans=0,sum=pw2[k-2];
    		for (int i=n-1;i>=1;i--)
    			if (n-i-1<=k-2)
    				ans=(ans+1LL*a[n-i]*pw[i-1]%MOD*pw2[n-i-1])%MOD;
    			else
    			{
    				sum=(sum*2LL-C(n-i-2,k-2))%MOD;
    				ans=(ans+1LL*a[n-i]*pw[i-1]%MOD*sum)%MOD;	
    			}
    		sum=pw2[k-1];
    		for (int i=n;i>=1;i--)
    			if (n-i<=k-1)
    				ans=(ans+1LL*(a[n-i+1]-a[n-i])*pw[i-1]%MOD*pw2[n-i])%MOD;
    			else
    			{
    				sum=(sum*2LL-C(n-i-1,k-1))%MOD;
    				ans=(ans+1LL*(a[n-i+1]-a[n-i])*pw[i-1]%MOD*sum)%MOD;
    			}
    		cout<<(ans%MOD+MOD)%MOD<<"
    ";
    	}
    	return 0;
    }
    
  • 相关阅读:
    2017.5.11下午学习内容
    windows消息和消息队列
    探索Win32系统之窗口类(转载)
    WinMain函数详解(转载)
    Ajax爬取实战头条街拍美图
    Ajax实战微博
    Ajax请求分析实战
    ubuntu 安装rails
    ubuntu Thunderbird 接收邮件显示乱码的问题排除
    ubuntu 开机挂载windows分区
  • 原文地址:https://www.cnblogs.com/stoorz/p/15134094.html
Copyright © 2011-2022 走看看