牛客 - 求函数(线段树+区间合并/线段树+矩阵维护)
生活随笔
收集整理的這篇文章主要介紹了
牛客 - 求函数(线段树+区间合并/线段树+矩阵维护)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目鏈接:點擊查看
題目大意:現在有 n 個函數,每個函數都是諸如 f( x ) = k * x + b 的形式,只是每個函數的 k 和 b 都是相互獨立的,現在給出兩個操作:
題目分析:算半個數學問題吧,第一步是需要將題目中的公式轉換一下,因為直接遞歸模擬肯定是要超時的,因為題目也給到了提示,就是用 l 和 r 表示下標,很容易聯想到區間問題,這樣操作一就是單點更新,操作二就是區間查詢了,也就是需要借助線段樹來實現,想到了這一步的話,還是化簡一下公式吧,其實自己在紙上算兩下應該就能算出來了,比較簡單:
注意上面公式中的 j == r + 1時的 k_j == 1
然后就是考慮加法兩邊的部分分別用兩個線段樹來維護,方便起見我們分別表示為 tree1 和 tree2 ,現在需要考慮如何合并:
因為前半部分是累乘,所以直接累乘就好了:
tree1[ k ] = tree1[ k << 1 ] * tree1[ k << 1 | 1 ]
后半部分的話稍微麻煩一點,在紙上畫一下會發現左兒子需要再乘上右兒子 tree1 的答案,也就是:
tree2[ k ] = tree2[ k << 1 ] * tree1[ k << 1 | 1 ]+ tree2[ k << 1 | 1 ]
剩下的實現就好了,兩棵樹維護起來比較麻煩,不過還是有技巧可尋的
2020.2.7更新:
水群的時候發現還可以用矩陣來描述轉移,因為
即
顯然可以發現對于矩陣存在轉移關系,用線段樹維護就好了,實現起來比區間合并簡單了好多
代碼:
線段樹+區間合并:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<unordered_map> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=2e5+100;const int mod=1e9+7;int K[N],B[N];struct Node {int l,r;LL sum; }tree1[N<<2],tree2[N<<2];void build(int k,int l,int r) {tree1[k].l=tree2[k].l=l;tree1[k].r=tree2[k].r=r;if(l==r)return;int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); }void update(int k,int pos,int K,int B) {if(tree1[k].l==tree1[k].r){tree1[k].sum=K;tree2[k].sum=B;return;}int mid=tree1[k].l+tree1[k].r>>1;if(mid>=pos)update(k<<1,pos,K,B);elseupdate(k<<1|1,pos,K,B);tree1[k].sum=tree1[k<<1].sum*tree1[k<<1|1].sum%mod;tree2[k].sum=(tree2[k<<1].sum*tree1[k<<1|1].sum%mod+tree2[k<<1|1].sum)%mod; }pair<LL,LL> merge(pair<LL,LL> a,pair<LL,LL> b)//左半段和右半段合并 {return make_pair(a.first*b.first%mod,(a.second*b.first%mod+b.second)%mod); }pair<LL,LL> query(int k,int l,int r) {if(tree1[k].r<l||tree1[k].l>r)return make_pair(1,0);if(tree1[k].l>=l&&tree1[k].r<=r)return make_pair(tree1[k].sum,tree2[k].sum);return merge(query(k<<1,l,r),query(k<<1|1,l,r)); }int main() { //#ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); //#endif // ios::sync_with_stdio(false);int n,m;scanf("%d%d",&n,&m);build(1,1,n);for(int i=1;i<=n;i++)scanf("%d",K+i);for(int i=1;i<=n;i++)scanf("%d",B+i);for(int i=1;i<=n;i++)update(1,i,K[i],B[i]);while(m--){int op;scanf("%d",&op);if(op==1){int pos,k,b;scanf("%d%d%d",&pos,&k,&b);update(1,pos,k,b);}else{int l,r;scanf("%d%d",&l,&r);pair<LL,LL>ans=query(1,l,r);printf("%lld\n",(ans.first+ans.second)%mod);}}return 0; }線段樹+矩陣:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<unordered_map> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=2e5+100;const int mod=1e9+7;struct Ma {LL a[2][2];Ma(){memset(a,0,sizeof(a));}static Ma ONE()//返回單位矩陣 {Ma ans;for(int i=0;i<2;i++)ans.a[i][i]=1;return ans;}friend Ma operator * (const Ma& a,const Ma& b){Ma ans;for(int i=0;i<2;i++)for(int j=0;j<2;j++){ans.a[i][j]=0;for(int k=0;k<2;k++)ans.a[i][j]+=a.a[i][k]*b.a[k][j];ans.a[i][j]%=mod;}return ans;} };struct Node {int l,r;Ma node; }tree[N<<2];void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;tree[k].node.a[0][1]=0;tree[k].node.a[1][1]=1;if(l==r)return;int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); }void update(int k,int pos,int K,int B) {if(tree[k].l==tree[k].r){tree[k].node.a[0][0]=K;tree[k].node.a[1][0]=B;return;}int mid=tree[k].l+tree[k].r>>1;if(mid>=pos)update(k<<1,pos,K,B);elseupdate(k<<1|1,pos,K,B);tree[k].node=tree[k<<1].node*tree[k<<1|1].node; }Ma query(int k,int l,int r) {if(tree[k].l>r||tree[k].r<l)return Ma::ONE();if(tree[k].l>=l&&tree[k].r<=r)return tree[k].node;return query(k<<1,l,r)*query(k<<1|1,l,r); }int a[N];int main() { //#ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); // freopen("output.txt","w",stdout); //#endif // ios::sync_with_stdio(false);int n,m;scanf("%d%d",&n,&m); build(1,1,n);for(int i=1;i<=n;i++)scanf("%d",a+i);for(int i=1;i<=n;i++){int x;scanf("%d",&x);update(1,i,a[i],x);}while(m--){int op;scanf("%d",&op);if(op==1){int pos,k,b;scanf("%d%d%d",&pos,&k,&b);update(1,pos,k,b);}else{int l,r;scanf("%d%d",&l,&r);Ma ans=query(1,l,r);printf("%lld\n",(ans.a[0][0]+ans.a[1][0])%mod);}} return 0; }?
總結
以上是生活随笔為你收集整理的牛客 - 求函数(线段树+区间合并/线段树+矩阵维护)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牛客 - 建通道(思维)
- 下一篇: HDU - 4641 K-string(