• 「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;
    }
    
  • 相关阅读:
    Python开发【第二十一篇】:Web框架之Django【基础】
    梳理
    Python Day15 jQuery
    day12 html基础
    Python day11 Mysql
    锻炼马甲线
    第一章:软件性能测试基本概念
    第4关—input()函数
    第3关—条件判断与嵌套
    第2关—数据类型与转换
  • 原文地址:https://www.cnblogs.com/HenryHuang-Never-Settle/p/solution-CF446C.html
走看看 - 开发者的网上家园