• P8340[AHOI2022]山河重整【dp,倍增】


    正题

    题目链接:https://www.luogu.com.cn/problem/P8340


    题目大意

    给出一个\(n\)和模数\(P\)。求有多少个在\(1\sim n\)中选择若干个数的集合\(S\),满足\(1\sim n\)中的每个数都可以表示成\(S\)的某个子集的和。

    \(1\leq n\leq 5\times 10^5,2\leq P\leq 1.1\times 10^9\)


    解题思路

    先考虑集合\(S\)符合要求的条件。

    我们从小到大加入\(S\)集合中的数,那么假设我们目前能表示\(1\sim k\),那么在我们加入一个数字\(x\)时,显然要求\(x\leq k+1\),否则\(k+1\)就无法被表示。那么我们在加入\(x\)后我们能表示的范围就变成了\(1\sim x+k\)

    那么这个条件已经很显然,对于任意的\(i\in[1,n]\),要求\(\leq i\)的数字和\(\geq i\)即可。

    考虑减去不合法的方案,记\(f_i\)表示恰好在位置\(i\)\(\leq i\)的数字和为\(i\)且前面的都合法,那么我们不选\(i+1\)即可。

    那么这个\(f_i\)怎么计算,因为要求前面的位置都合法,那么我们继续考虑减去不合法的方案。我们先算出\(i\)的整数划分,然后不合法的方案我们考虑枚举第一个不合法的位置。和上面的类似,我们枚举一个\(j\),然后前面的方案就是\(f_j\),之后\(j+1\)不选,那么剩下\(j+2\sim i\)中选择若干个数使得其和\(j\)的和为\(i\)

    先考虑整数拆分的方案,这个好说,因为划分的每个数字都要求不同,所以数字总数是\(\sqrt n\)级别的。那么这就很好解决了,我们设\(g_{i,j}\)表示目前和为\(i\),选了\(j\)个数字,那么我们每次要么多一个数字,要么所有的数字一起加\(1\)就好了。

    然后考虑后面那个转移,因为\(f_j+sum=i,sum\geq j+2\),也就是说\(2j+2\leq i\)。那么我们可以考虑一个倍增的做法,每次先处理\(1\sim i\)然后再处理\(1\sim 2i\)

    之后考虑怎么快速计算左边对右边的贡献,考虑用上面类似的方法,不过我们再每次处理完后要令\(g_{j+(j+2)\times i}+=f_j\)

    时间复杂度:\(O(n\sqrt n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=5e5+10;
    ll n,P,h[N],f[N];
    void add(ll &x,ll y)
    {x=((x+y<P)?(x+y):(x+y-P));}
    signed main()
    {
    	scanf("%lld%lld",&n,&P);
    	for(ll i=n;i>=1;i--){
    		if(i*(i+1)/2>n)continue;
    		for(ll j=n;j>=i;j--)f[j]=f[j-i];
    		f[i]++;
    		for(ll j=i;j<=n;j++)add(f[j],f[j-i]);
    	}
    	memset(h,0,sizeof(h));f[0]=1;
    	for(ll l=1,r;l<n;l=r){
    		r=min(l*2+2,n);
    		for(ll i=l;i>=1;i--){
    			if(i*(i+1)/2>r)continue;
    			for(ll j=r;j>=i;j--)h[j]=h[j-i];
    			for(ll j=0;j+(j+2)*i<=r;j++)add(h[j+(j+2)*i],f[j]);
    			for(ll j=i;j<=r;j++)add(h[j],h[j-i]);
    		}
    		for(ll i=l+1;i<=r;i++)add(f[i],P-h[i]);
    		for(ll i=1;i<=r;i++)h[i]=0;
    	}
    	ll ans=1;
    	for(ll i=1;i<=n;i++)ans=ans*2ll%P;
    	for(ll i=n-1,pw=1;i>=0;i--,pw=pw*2ll%P)
    		add(ans,P-pw*f[i]%P);
    	printf("%lld\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    C# 下 WebService 初探: 构建Web Service 服务及 WinForm和浏览器 httpget调用
    Action<T> 泛型委托 在跨线程访问控件委托中的应用
    C# 网络连接中异常断线的处理:ReceiveTimeout, SendTimeout 及 KeepAliveValues(设置心跳)
    DOS下的批处理文件(老东西了)
    typedef struct和struct定义结构体的区别
    多线程idhttp下载文件源代码
    JQUERY获取DOM对象
    学习笔记:delphi实现网络通信之select模型
    学习笔记之WSAAsyncSelect模式
    delphi 函数指针 方法指针
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/16305033.html
Copyright © 2020-2023  润新知