「学习笔记」 zkw线段树-Summar Vacation Trainning Day3
主要可以用来代替树状数组。
所以大部分时候我还是用普通线段数咧
其核心思想就是用两个"收缩"节点自底而上修改或查询。
并且区修区查时用 pushdown 会导致常数变劣,这时就使用标记永久化。
以下是区修区查。
Code(附注释):
#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN=1e6+5;
#define ll long long
#define int long long
int n,q,p;
ll t[MAXN<<2],a[MAXN<<2],laz[MAXN<<2];
void bui(){
p=1;
while(p<n+2) p<<=1;
for(int i=p+1;i<=p+n;i++){
t[i]=a[i-p];
}
for(int i=p+n+1;i>=2;i--){
t[i>>1]+=t[i];
}
}
void add(int l,int r,ll x){
l=l+p-1,r=r+p+1;ll now=1;
// printf("CAO:%d %d\n",l,r);
for(;(l^r)!=1;l>>=1,r>>=1,now<<=1){
if(!(l&1)) t[l^1]+=now*x,laz[l^1]+=x;//在另外一端没有直接加答案的节点
if(r&1) t[r^1]+=now*x,laz[r^1]+=x;//同理
t[l>>1]=t[l]+t[l^1]+laz[l>>1]*(now<<1);
t[r>>1]=t[r]+t[r^1]+laz[r>>1]*(now<<1);
}
for(;l>=1;l>>=1,now<<=1) t[l>>1]=t[l]+t[l^1]+laz[l>>1]*(now<<1);
}
ll sum(int l,int r){
l=l+p-1,r=r+p+1;ll res=0,s1=0,s2=0,now=1;
// printf("JJJ:%d %d\n",l,r);
for(;(l^r)!=1;l>>=1,r>>=1,now<<=1){
if(!(l&1)) res+=t[l^1],s1+=now;
if(r&1) res+=t[r^1],s2+=now;
res+=laz[l>>1]*s1+laz[r>>1]*s2;//加上懒标乘管理的区间
}
// printf("CAO:%d %d %lld\n",s1,s2,res);
for(;l>=1;l>>=1) res+=laz[l>>1]*(s1+s2);
return res;
}
signed main(){
scanf("%lld%lld",&n,&q);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
bui();
// printf("%lld\n",sum(1,n));
while(q--){
int op;
scanf("%lld",&op);
if(op==1){
int l,r;ll x;scanf("%lld%lld%lld",&l,&r,&x);add(l,r,x);
}
else{
int l,r;scanf("%lld%lld",&l,&r);printf("%lld\n",sum(l,r));
}
}
return 0;
}
Sasha and Array
矩阵运算有结合律,所以把线段树上的节点当成矩阵来处理即可。
注意这里的懒标是乘,处理方式会有改变。
Code:
#include<cstdio>
#include<iostream>
using namespace std;
const int Mod=1e9+7;
const int MAXN=1e5+5;
#define int long long
int n,m,a[MAXN],p;
struct Matrix{
int f[4][4];
void clear()
{
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
f[i][j]=0;
}
}
}
void init()
{
for(int i=0;i<4;i++){
f[i][i]=1;
}
}
bool empty(){
if(f[1][1]!=1) return 0;
if(f[1][2]!=0) return 0;
if(f[2][1]!=0) return 0;
if(f[2][2]!=1) return 0;
return 1;
}
Matrix operator*(const Matrix y)
{
Matrix z;
z.clear();
for(int i=1;i<=2;i++){
for(int j=1;j<=2;j++){
for(int k=1;k<=2;k++){
z.f[i][j]=(z.f[i][j]+(f[i][k]%Mod)*(y.f[k][j]%Mod))%Mod;
}
}
}
return z;
}
Matrix operator+(const Matrix y)
{
Matrix z;
z.clear();
for(int i=1;i<=2;i++){
for(int j=1;j<=2;j++){
z.f[i][j]=f[i][j]+y.f[i][j]%Mod;
}
}
return z;
}
}base,ans;
void Init()
{
ans.clear();
base.clear();
ans.f[1][1]=ans.f[1][2]=1;
ans.f[2][1]=ans.f[2][2]=0;
base.f[1][2]=base.f[2][2]=base.f[2][1]=1;
base.f[1][1]=0;
return;
}
struct SG{
Matrix sum,tag;
}t[MAXN<<2];
Matrix ksm(Matrix a,int b)
{
Matrix Ans;
Ans.clear();
Ans.init();
while(b)
{
if(b&1)
{
Ans=Ans*a;
}
a=a*a;
b>>=1;
}
return Ans;
}
void Build()
{
p=1;
while(p<n+2) p<<=1;
for(int i=1;i<=n;i++){
t[i+p].sum=ans*ksm(base,a[i]-1);
}
for(int i=p+n+1;i>=1;i--){
t[i>>1].sum=t[i>>1].sum+t[i].sum;
for(int j=1;j<=2;j++){
t[i].tag.f[j][j]=1;
}
}
}
void Modify(int l,int r,Matrix x)
{
l=l+p-1,r=r+p+1;
for(;(l^r)!=1;l>>=1,r>>=1){
if(!(l&1)) t[l^1].sum=t[l^1].sum*x,t[l^1].tag=t[l^1].tag*x;
if(r&1) t[r^1].sum=t[r^1].sum*x,t[r^1].tag=t[r^1].tag*x;
t[l>>1].sum=(t[l].sum+t[l^1].sum)*(t[l>>1].tag);
t[r>>1].sum=(t[r].sum+t[r^1].sum)*(t[r>>1].tag);
}
for(;l>1;l>>=1) t[l>>1].sum=(t[l].sum+t[l^1].sum)*(t[l>>1].tag);
}
Matrix Search(int l,int r)
{
l=l+p-1,r=r+p+1;Matrix s1,s2;s1.clear();s2.clear();
for(;(l^r)!=1;l>>=1,r>>=1){
if(!(l&1)) s1=s1+t[l^1].sum;
if(r&1) s2=s2+t[r^1].sum;
s1=s1*t[l>>1].tag;s2=s2*t[r>>1].tag;
}
s1=s1+s2;//已经到达同一深度
for(;l>1;l>>=1) s1=s1*t[l>>1].tag;
return s1;
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
Init();
Build();
while(m--)
{
int op;
scanf("%lld",&op);
if(op==1)
{
int l,r,x;
scanf("%lld%lld%lld",&l,&r,&x);
Matrix ppp=ksm(base,x);
Modify(l,r,ppp);
continue;
}
int l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",Search(l,r).f[1][1]%Mod);
}
return 0;
}
Power Sockets
贪心容易想到从长到短放,每次折半放。
以深度为下标建线段树即可。
可以线段树上二分操作。
于是直接上普通线段树咧
Code:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int MAXN=1e6+5;
const int MAXM=1e6;
const int INF=0x3f3f3f3f;
int n,a[MAXN],res=INF;
ll k;
bool cmp(int x,int y){return x>y;
}
struct SG{
struct node{
int l,r;ll sum,laz;
}t[MAXN<<2];
void pup(int p){t[p].sum=t[p<<1].sum+t[p<<1|1].sum;}
void pdo(int p){
if(!t[p].laz) return;
int mid=(t[p].l+t[p].r)>>1;
t[p<<1].sum+=t[p].laz*(mid-t[p].l+1),t[p<<1].laz+=t[p].laz;
t[p<<1|1].sum+=t[p].laz*(t[p].r-mid),t[p<<1|1].laz+=t[p].laz;
t[p].laz=0;
}
void bui(int p,int l,int r){
t[p].l=l,t[p].r=r;t[p].sum=0;t[p].laz=0;
if(l==r) return;
int mid=(l+r)>>1;
bui(p<<1,l,mid);bui(p<<1|1,mid+1,r);
}
void modi(int p,int l,int r,int x){
if(t[p].l>=l&&t[p].r<=r){
t[p].sum+=(t[p].r-t[p].l+1)*x;t[p].laz+=x;return;
}
pdo(p);
int mid=(t[p].l+t[p].r)>>1;
if(l<=mid) modi(p<<1,l,r,x);
if(r>mid) modi(p<<1|1,l,r,x);
pup(p);
}
ll que(int p,int l,int r){
if(t[p].l>=l&&t[p].r<=r) return t[p].sum;
pdo(p);ll tmp=0;
int mid=(t[p].l+t[p].r)>>1;
if(l<=mid) tmp+=que(p<<1,l,r);
if(r>mid) tmp+=que(p<<1|1,l,r);
return tmp;
}
int sea(int p){
pdo(p);
if(t[p].l==t[p].r) return t[p].l;
if(t[p<<1].sum) return sea(p<<1);
else return sea(p<<1|1);
}
int res(int p,ll k){
pdo(p);
if(t[p].l==t[p].r) return t[p].l;
if(t[p<<1].sum>=k) return res(p<<1,k);
else return res(p<<1|1,k-t[p<<1].sum);
}
}T;
int main(){
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n,cmp);
T.bui(1,1,MAXM);
for(int i=1;i<=n;i++){
int op=0;
if(i==1) op=0;
else op=T.sea(1),T.modi(1,op,op,-1);
if(a[i]&1) T.modi(1,op+2,op+a[i]/2+1,2);
else T.modi(1,op+2,op+a[i]/2,2),T.modi(1,op+a[i]/2+1,op+a[i]/2+1,1);
if(T.que(1,1,MAXM)>=k){
res=min(res,T.res(1,k));
}
}
printf("%d",res==INF?-1:res);
return 0;
}
Greedy Shopping
容易发现修改后数列仍不降,那么线段树上二分即可。
注意 laz 标的意义是区间覆盖,不要写成 +=。
Code:
#include<cstdio>
#include<iostream>
using namespace std;
#define ll long long
const int MAXN=3e5+5;
int n,m;
ll a[MAXN];
struct SG{
struct node{
int l,r;ll sum,ma,laz;
}t[MAXN<<2];
void pup(int p){
t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
t[p].ma=min(t[p<<1].ma,t[p<<1|1].ma);
}
void pdo(int p){
if(!t[p].laz) return;
int mid=(t[p].l+t[p].r)>>1;
t[p<<1].sum=(mid-t[p].l+1)*t[p].laz;t[p<<1].laz=t[p].laz;t[p<<1].ma=t[p].laz;
t[p<<1|1].sum=(t[p].r-mid)*t[p].laz;t[p<<1|1].laz=t[p].laz;t[p<<1|1].ma=t[p].laz;
t[p].laz=0;
}
void bui(int p,int l,int r){
t[p].l=l,t[p].r=r;
if(l==r){
t[p].sum=t[p].ma=a[l];return;
}
int mid=(l+r)>>1;
bui(p<<1,l,mid);bui(p<<1|1,mid+1,r);
pup(p);
}
void modi(int p,int l,int r,ll x){
if(t[p].l>=l&&t[p].r<=r){
t[p].sum=x*(t[p].r-t[p].l+1);t[p].ma=x;t[p].laz=x;return;
}
pdo(p);
int mid=(t[p].l+t[p].r)>>1;
if(l<=mid) modi(p<<1,l,r,x);
if(r>mid) modi(p<<1|1,l,r,x);
pup(p);
}
int sea(int p,ll d){
pdo(p);
if(t[p].l==t[p].r) return t[p].l;
if(t[p<<1].ma<=d) return sea(p<<1,d);
if(t[p<<1|1].ma<=d) return sea(p<<1|1,d);
return n+1;
}
int que(int p,int l,ll &d)
{
pdo(p);
if(t[p].ma>d) return 0;
int mid=(t[p].l+t[p].r)>>1,tmp=0;
if(t[p].l>=l){
if(d>=t[p].sum){
d-=t[p].sum;return (t[p].r-t[p].l+1);
}
}
if(l<=mid) tmp+=que(p<<1,l,d);
tmp+=que(p<<1|1,l,d);
return tmp;
}
}T;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
T.bui(1,1,n);
while(m--){
int op,x;ll y;
scanf("%d%d%lld",&op,&x,&y);
if(op==1){
int wz=T.sea(1,y);
if(wz>x) continue;
T.modi(1,wz,x,y);
}
else{
printf("%d\n",T.que(1,x,y));
}
}
return 0;
}
本文来自博客园,作者:{StranGePants},转载请注明原文链接:https://www.cnblogs.com/StranGePants/p/16448529.html