• 「CF446C」 DZY Loves Fibonacci Numbers


    「CF446C」 DZY Loves Fibonacci Numbers

    这里提供一种优美的根号分治做法。

    首先,我们考虑一种不太一样的暴力。对于一个区间加斐波那契数的操作 ([a,b]),以及一个区间求和的操作 ([p,q]),仅需预处理斐波那契数列前缀和,我们就可以在 (O(1)) 的时间内算出 ([a,b])([p,q]) 的贡献。

    这样的复杂度为 (O(n^2))

    再考虑传统暴力:对于一个区间加斐波那契数的操作 ([a,b]),直接将其作用到原数列 (a) 上。

    那么我们可不可以将多个区间加斐波那契数的操作一起作用到原数列上呢?

    答案是肯定的。

    首先有一个显然的结论:若 (a_i=a_{i-1}+a_{i-2},b_j=b_{j-1}+b_{j-2}),则有 (a_i+b_j=a_{i-1}+a_{i-2}+b_{j-1}+b_{j-2})

    这启发我们可以对于多次区间加同时进行递推。

    我们维护每个区间加操作的起止点,在操作开始时加入数 (1) 进行递推,操作结束时删除该操作所用到的两个数,就可以在 (O(n)) 的时间内将多次操作同时进行。

    具体可见代码,非常清晰。

    若我们每 (T) 次操作后将当前的多个区间加斐波那契数的操作一起作用到原数列上,其余时候暴力,这样的最坏复杂度为 (O(frac{n^2}{T}+Tn))。取 $T=sqrt n $ 时复杂度最优,为 (O(nsqrt n))。虽在理论复杂度上逊于线段树解法,但其常数小,代码复杂度低,实际表现与实现一般的线段树相差无几甚至略优,不失为一种良好的解题方式。

    同时注意,块长的调整可能会使该算法的效率进一步提升,因为显然 (O(Tn)) 部分是达不到上界的,故代码对于重构的实现方式与题解略有不同。

    贴代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int p=1e9+9;
    const int maxn=3e5+5;
    int fib[maxn],sum[maxn];
    int b[maxn],v[maxn],val[maxn];
    int l[maxn],r[maxn],tot;
    int n,m;
    int md(int x){
    	if(x>p) return x-p;
    	if(x<0) return x+p;
    	return x;
    }
    vector<int> p1[maxn],p2[maxn];
    void rebuild(){
    	for(int i=1;i<=tot;++i){
    		p1[l[i]].emplace_back(i);
    		p2[r[i]].emplace_back(i);
    	}
    	int a=0,b=0;
    	for(int i=1;i<=n;++i){
    		int c=md(a+b);
    		val[i]=md(val[i]+c);
    		a=b,b=c;
    		for(auto x:p1[i]) b=md(b+1),val[i]=md(val[i]+1);
    		for(auto x:p2[i]){
    			int L=l[x],R=r[x];
    			b=md(b-fib[R-L+1]);
    			a=md(a-fib[R-L]);
    		}
    		v[i]=md(v[i-1]+val[i]);
    	}
    	for(int i=1;i<=tot;++i){
    		p1[l[i]].clear();
    		p2[r[i]].clear();
    	}
    	tot=0;
    }
    int main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0);
    	cin>>n>>m;
    	fib[1]=1,fib[2]=1;
    	for(int i=3;i<=n;++i) fib[i]=md(fib[i-1]+fib[i-2]);
    	for(int i=1;i<=n;++i) sum[i]=md(sum[i-1]+fib[i]);
    	for(int i=1;i<=n;++i) cin>>val[i],v[i]=md(v[i-1]+val[i]);
    	int lim=sqrt(m);
    	for(int _=1;_<=m;++_){
    		int opt,a,b;cin>>opt>>a>>b;
    		if(opt==1) l[++tot]=a,r[tot]=b;
    		else{
    			int ans=0;
    			for(int i=1;i<=tot;++i){
    				int L=max(a,l[i]),R=min(b,r[i]);
    				if(L<=R){
    					ans=md(ans+md(sum[R-l[i]+1]-sum[L-l[i]]));
    					if(ans>p) ans-=p;
    				}
    			}
    			cout<<md(ans+md(v[b]-v[a-1]))<<'
    ';
    		}
    		if(tot==lim) rebuild();
    	}
    	return 0;
    }
    
  • 相关阅读:
    记录下我常用的工具
    记录下Lambda常用的表现形式
    链式编程学习之练习篇
    MySQL5.6.35部署
    jdk+Tomcat环境
    查找Linux中内存和CPU使用率最高的进程
    Linux 双网卡绑定
    saltstack 把数据返回到mysql服务器
    Python中map,filter,reduce,zip的应用
    python使用psutil获取服务器信息
  • 原文地址:https://www.cnblogs.com/HenryHuang-Never-Settle/p/solution-CF446C.html
Copyright © 2020-2023  润新知