Codeforces718C Sasha and Array题解(线段树+矩阵加速)

本苟蒻第一次写题解,有不对的地方希望大家能够指出。

题目链接 Sasha and Array

题意:给定n个元素,每次可以进行两种操作

1.将[l,r]元素均增加x

 2.求 f(x)代表斐波那契数列中第x个元素的值

题解:看到区间修改首先想到线段树,对于第二个操作,我们可以考虑矩阵加速,对于线段树每个区间,我们维护一个矩阵表示区间和的当前以及后继状态,懒标记也用一个矩阵表示(实际上也可以用一个数维护幂次,但取余时会比较复杂),懒标记下沉时可以直接表示为矩阵相乘,合并时因为是求和所以表示为矩阵相加。

细节可见代码注释

注:矩阵部分建议写成一整个结构体。


#include<iostream>
#include<stack>
#include<list>
#include<set>
#include<vector>
#include<algorithm>
#include<math.h>
#include<numeric>
#include<map>
#include<cstring>
#include<queue>
#include<iomanip>
#include<cmath>
#include<queue>
#include <bitset>
#include<unordered_map>
	#ifndef local
	#define endl '\n'
#endif */
#define mkp make_pair
using namespace std;
using std::bitset;
typedef long long ll;
typedef long double ld;
const int inf=0x3f3f3f3f;
const ll MAXN=2e6+10;
const ll N=1e5+100;
const ll mod=1e9+7;
const ll hash_p1=1610612741;
const ll hash_p2=805306457;
const ll hash_p3=402653189;
//-----------------------------------------------------------------------------------------------------------------*/
// ll head[MAXN],net[MAXN],to[MAXN],edge[MAXN]/*流量*/,cost[MAXN]//费用;
/* 
void add(ll u,ll v,ll w,ll s){
	to[++cnt]=v;net[cnt]=head[u];edge[cnt]=w;cost[cnt]=s;head[u]=cnt;
	to[++cnt]=u;net[cnt]=head[v];edge[cnt]=0;cost[cnt]=-s;head[v]=cnt;
}
struct elemt{
	int p,v;
};
struct comp{
	public:
		bool operator()(elemt v1,elemt v2){
			return v1.v<v2.v;
		}
};
-----------------------------------
求[1,MAXN]组合式和逆元 
ll mi(ll a,ll b){
	ll res=1;
	while(b){
		if(b%2){
			res=res*a%mod;
		}	
		a=a*a%mod;
	}
	return res;
}
ll fac[MAXN],inv[MAXN]
fac[0]=1;inv[0]=1;
for(int i=1;i<=MAXN;i){
	fac[i]=(fac[i-1]*i)%mod;
	inv[i]=mi(fac[i],mod-2);
}
ll C(int m,int n){//组合式C(m,n); 
	if(!n){
		return 1;
	}
	return fac[m]*(inv[n]*inv[m*-n]%mod)%mod;
}
---------------------------------
 unordered_map<int,int>mp;
//优先队列默认小顶堆 , greater<int> --小顶堆  less<int> --大顶堆  
priority_queue<elemt,vector<elemt>,comp>q;  
	set<int>::iterator it=st.begin();
*/
// vector<vector<int>>edge; 二维虚拟储存坐标 
//-----------------------------------------------------------------------------------------------------------------*/
  //map<int,bool>mp[N]; 
const int len=2;//矩阵大小
struct square{
	ll m[3][3];
	void clear(){//清空矩阵 
		for(int i=0;i<len;i++){
			for(int j=0;j<len;j++){
				m[i][j]=0;
			}
		}
	}
	void init(){//初始化,根据具体题意确定(这默认单位矩阵) 
		for(int i=0;i<len;i++){
			m[i][i]=1;
		}
	}
	void print(){
		for(int i=0;i<len;i++){
			for(int j=0;j<len;j++){
				cout<<"i="<<i<<" j="<<j<<" m="<<m[i][j]<<endl;
			}
		}
	}
	bool empty(){//判断是否为单位矩阵 
		for(int i=0;i<len;i++){
			for(int j=0;j<len;j++){
				if(m[i][j]&&i!=j||m[i][j]!=1&&i==j){
					return 0;
				}
			}
		} 
		return 1;
	}
	square operator*(const square &y)const{//矩阵乘法 
		square z;z.clear();
		for(int i=0;i<len;i++){
			for(int j=0;j<len;j++){
				for(int k=0;k<len;k++){
					z.m[i][j]=(z.m[i][j]+m[i][k]*y.m[k][j]%mod)%mod;
				}
			}
		}
		return z;
	} 
	friend square operator+(square a,square b){
		square z;z.clear();
		for(int i=0;i<len;i++){
			for(int j=0;j<len;j++){
				z.m[i][j]=(a.m[i][j]+b.m[i][j])%mod;
			}
		}
		return z;
	} 
};
int a[N];
square f;//递推矩阵
/*
1 1
1 0
*/ 
square base;//初始矩阵 ,m[1][1](i)代表当前 
/*
3 (i+2)    2 (i+1)
2 (i+1)    1 (i)
*/
square mi(square a,ll b){//矩阵快速幂 
	square res;res.clear();res.init();
	while(b){
		if(b%2){
			res=res*a;
		}
		a=a*a;
		b/=2;
	}
	return res;
}
square t[4*N];
square lz[4*N];//懒标记为矩阵,可以不用担心取模的细节 
void pushup(int rt){ 
	t[rt]=t[2*rt]+t[2*rt+1];
}
void build(int l,int r,int rt){
	t[rt].clear();
	lz[rt].clear();
	lz[rt].init();
	if(l==r){
		t[rt]=base*mi(f,a[l]-1);
		return ;
	}
	int mid=(l+r)/2;
	build(l,mid,2*rt);
	build(mid+1,r,2*rt+1);
	pushup(rt);
}
void pushdown(int rt){//懒标记下沉 
	if(t[rt].empty()){//为单位矩阵,说明无下沉标记 
		return ;
	}
	t[2*rt]=t[2*rt]*lz[rt];      
	t[2*rt+1]=t[2*rt+1]*lz[rt];
	lz[2*rt]=lz[2*rt]*lz[rt]; 
	lz[2*rt+1]=lz[2*rt+1]*lz[rt];
	lz[rt].clear();
	lz[rt].init();
}
void update(int l,int r,int le,int ri,int rt,int x){
	if(le<=l&&ri>=r){
		t[rt]=t[rt]*mi(f,x);
		lz[rt]=lz[rt]*mi(f,x);
		return ;
	}
	pushdown(rt);
	int mid=(l+r)/2;
	if(le<=mid){
		update(l,mid,le,ri,2*rt,x);
	}
	if(ri>mid){
		update(mid+1,r,le,ri,2*rt+1,x);
	}
	pushup(rt);
}
square query(int l,int r,int le,int ri,int rt){
	square res;res.clear();
	if(le<=l&&ri>=r){
		return t[rt];
	}
	pushdown(rt);
	int mid=(l+r)/2;
	if(le<=mid){
		res=res+query(l,mid,le,ri,2*rt);
	}
	if(ri>mid){
		res=res+query(mid+1,r,le,ri,2*rt+1);
	}
	return res;
}
int main(){
/*cout<<setiosflags(ios::fixed)<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(不含整数部分)*/
/*cout<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(含整数部分)*/
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);//同步流
f.clear();base.clear();
f.m[0][0]=f.m[0][1]=f.m[1][0]=1;                           //此部分为初始化初始矩阵 
base.m[0][0]=2;base.m[0][1]=base.m[1][0]=base.m[1][1]=1;
//------------------------------------------------------------------数据录入 
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
	cin>>a[i];
}
//--------------------------------------------------------------------
build(1,n,1);
while(m--){
	int k,l,r,x;
	cin>>k;
	if(k==1){
		cin>>l>>r>>x;
		update(1,n,l,r,1,x);
	}
	else{
		cin>>l>>r;
		cout<<query(1,n,l,r,1).m[1][1]<<endl;
	}
}
	return 0;
}

引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值