Loading

7.18 学习笔记

7.18 学习笔记

1 讲解内容

1.1 二维树状数组

实质上是一个树状数组的节点是一些树状数组,表示一些列的前缀和。而最外层树状数组表示横着的前缀和。

与一维树状数组的区别是加了一层循环。

1.2 二维线段树

2 例题

2.1 POJ 2155

二维树状数组,这个题维护每一个点被操作次数,二维差分后可以用二维树状数组。

水题。

// #include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 1010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int t,n,m;

struct TD_BIT{
    int p[N][N];
    inline int lowbit(int x){return x&(-x);}
    inline void insert(int x,int y,int k){
        for(int i=x;i<=n;i+=lowbit(i))
            for(int j=y;j<=n;j+=lowbit(j)) p[i][j]+=k;
    }
    inline int AskSum(int x,int y){
        int res=0;
        for(int i=x;i>=1;i-=lowbit(i))
            for(int j=y;j>=1;j-=lowbit(j)) res+=p[i][j];
        return res;
    }
    inline void clear(){
        memset(p,0,sizeof(p));
    }
};
TD_BIT bit;

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    while(t--){
        read(n);read(m);
        for(int i=1;i<=m;i++){
            char c;scanf("%c",&c);
            if(c=='C'){
                int x1,x2,y1,y2;read(x1);read(y1);read(x2);read(y2);
                bit.insert(x1,y1,1);bit.insert(x2+1,y1,-1);
                bit.insert(x1,y2+1,-1);bit.insert(x2+1,y2+1,1);
            }
            else{
                int x,y;read(x);read(y);
                int ans=bit.AskSum(x,y);
                if(ans%2) printf("1\n");
                else printf("0\n");
            }
        }
        bit.clear();
        if(t) putchar('\n');
    }
}

2.2 codeforces 1527 E

略带思维难度的动态规划优化 dp 。总体思路如下:

我们考虑拆贡献,把最后位置减最初位置拆成连续相邻几个位置相差之和。设一个颜色 \(a_i\) 的前驱为 \(pre_i\) 。我们考虑当到 \(i\) 时,位置 \([0,pre_i-1]\) 的贡献都会加 \(j-pre_j\) 。我们在更新 \(f_j\) 的时候,因为我们已经把前面的颜色处理好了,所以我们直接取前面最小值就可以。这个过程可以用线段树维护,并且一共要做 \(k\) 次。故时间复杂度为 \(O(nk\log n)\)

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 36000
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> T Min(T a,T b){
    return a<b?a:b;
}

template<typename T> T Max(T a,T b){
    return a<b?b:a;
}

int n,k,f[N],a[N],pre[N],w[N];

struct node{
    int minn,sum,len,lazy;
};

struct DS{
    node p[N<<2];
    inline void pushup(int k){
        p[k].minn=Min(p[k*2].minn,p[k*2+1].minn);
        p[k].sum=p[k*2].sum+p[k*2+1].sum;
        p[k].len=p[k*2].len+p[k*2+1].len;
    }
    inline void build(int k,int l,int r){
        if(l==r){p[k].lazy=0;p[k].sum=f[l];p[k].len=1;p[k].minn=f[l];return;}
        int mid=(l+r)>>1;p[k].lazy=0;
        build(k*2,l,mid);build(k*2+1,mid+1,r);
        pushup(k);
    }
    inline void A(int k,int x){
        p[k].lazy+=x;p[k].sum+=p[k].len*x;p[k].minn+=x;
    }
    inline void pushdown(int k){
        A(k*2,p[k].lazy);A(k*2+1,p[k].lazy);
        p[k].lazy=0;
    }
    inline void insert(int k,int l,int r,int z,int y,int x){
        if(l==z&&r==y){A(k,x);return;}
        if(p[k].lazy) pushdown(k);
        int mid=(l+r)>>1;
        if(y<=mid) insert(k*2,l,mid,z,y,x);
        else if(z>mid) insert(k*2+1,mid+1,r,z,y,x);
        else{insert(k*2,l,mid,z,mid,x);insert(k*2+1,mid+1,r,mid+1,y,x);}
        pushup(k);
    }
    inline int AskMin(int k,int l,int r,int z,int y){
        if(l==z&&r==y) return p[k].minn;
        if(p[k].lazy) pushdown(k);
        int mid=(l+r)>>1;
        if(y<=mid) return AskMin(k*2,l,mid,z,y);
        else if(z>mid) return AskMin(k*2+1,mid+1,r,z,y);
        else return Min(AskMin(k*2,l,mid,z,mid),AskMin(k*2+1,mid+1,r,mid+1,y));
    }
};
DS ds;

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(k);
    for(int i=1;i<=n;i++){
        read(a[i]);pre[i]=w[a[i]];w[a[i]]=i;
        if(!pre[i]) pre[i]=i;
    }
    // for(int i=1;i<=n;i++) printf("i:%d pre:%d\n",i,pre[i]);
    for(int i=1;i<=n;i++){
        f[i]=f[i-1]+i-pre[i];
    }
    // for(int i=1;i<=n;i++) printf("i:%d f:%d\n",i,f[i]);
    for(int i=2;i<=k;i++){
        ds.build(1,0,n);
        for(int j=i;j<=n;j++){
            if(pre[j]!=j){
                // printf("change:j:%d l:0 r:%d x:%d\n",j,pre[j]-1,j-pre[j]);
                ds.insert(1,0,n,0,pre[j]-1,j-pre[j]);
            } 
            // printf("askmin:j:%d l:%d r:%d \n",j,i-1,j-1);
            f[j]=ds.AskMin(1,0,n,i-1,j-1);
        }
        // for(int j=1;j<=n;j++) printf("i:%d f:%d\n",j,f[j]);
        // memset(ds.p,0,sizeof(ds.p));
    }
    printf("%d\n",f[n]);
    return 0;
}//

2.3 POJ 3728

考虑我们可以暴力维护并合并一条链上祖先减儿子和儿子减祖先的最大值,树上倍增就可以完成这个操作,合并两条链直接暴力合并就可以。如果带修改可以熟练剖分加线段树维护,时间复杂度 \(O(n\log n)\)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 50010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> T Max(T a,T b){
    return a<b?b:a;
}

template<typename T> T Min(T a,T b){
    return a<b?a:b;
}

struct edge{
    int to,next;
    inline void intt(int to_,int ne_){
        to=to_;next=ne_;
    }
};
edge li[N<<1];
int head[N],tail;

inline void add(int from,int to){
    li[++tail].intt(to,head[from]);
    head[from]=tail;
}

int a[N],n,q;
int Tmax[17][N],Tmin[17][N],up[17][N],down[17][N],fa[17][N],dep[N];
int lg2[N];

inline void dfs(int k,int fat){
    dep[k]=dep[fat]+1;
    int limit=lg2[dep[k]-1];

	if(fat!=0){
		fa[0][k]=fat;
		Tmax[0][k]=Max(a[k],a[fa[0][k]]);
		Tmin[0][k]=Min(a[k],a[fa[0][k]]);
		up[0][k]=Max(0,a[fa[0][k]]-a[k]);
		down[0][k]=Max(0,a[k]-a[fa[0][k]]);
	}

    for(int i=1;i<=limit;i++){
        fa[i][k]=fa[i-1][fa[i-1][k]];
        Tmax[i][k]=Max(Tmax[i-1][k],Tmax[i-1][fa[i-1][k]]);
        Tmin[i][k]=Min(Tmin[i-1][k],Tmin[i-1][fa[i-1][k]]);
        up[i][k]=Max(up[i-1][k],
            Max(up[i-1][fa[i-1][k]],Tmax[i-1][fa[i-1][k]]-Tmin[i-1][k]));
		// up[0][k],up[0][fa[0][k]] Tmax[0][fa[0][k]]-Tmin[0][k]
        down[i][k]=Max(down[i-1][k],
            Max(down[i-1][fa[i-1][k]],Tmax[i-1][k]-Tmin[i-1][fa[i-1][k]]));
    }

	/*
	printf("k:%d\n",k);printf("dep:%d\n",dep[k]);
	printf("fa:");for(int i=0;i<=limit;i++) printf("%d ",fa[i][k]);putchar('\n');
	printf("Tmax:");for(int i=0;i<=limit;i++) printf("%d ",Tmax[i][k]);putchar('\n');
	printf("Tmin:");for(int i=0;i<=limit;i++) printf("%d ",Tmin[i][k]);putchar('\n');
	printf("up:");for(int i=0;i<=limit;i++) printf("%d ",up[i][k]);putchar('\n');
	printf("down:");for(int i=0;i<=limit;i++) printf("%d ",down[i][k]);putchar('\n');
	putchar('\n');// seem to be all right.
	*/

    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(to==fat) continue;
        dfs(to,k);
    }
}

int MaxU=-INF,MaxD=-INF;
int maxx=-INF,minn=INF,MaxUD;
int maxup=-INF,minup=INF,maxdown=-INF,mindown=INF;

inline void Intt(){
	MaxU=-INF;MaxD=-INF;
	maxx=-INF;minn=INF;
	maxup=-INF;minup=INF;maxdown=-INF;mindown=INF;
}

inline void Update(bool op,int id,int x){
	if(op==1){
		MaxU=Max(MaxU,Tmax[id][x]-minn);
		MaxU=Max(MaxU,up[id][x]);
		minn=Min(minn,Tmin[id][x]);
	}
	else{
		MaxD=Max(MaxD,maxx-Tmin[id][x]);
		MaxD=Max(MaxD,down[id][x]);
		maxx=Max(maxx,Tmax[id][x]);
	}
}

inline int solve(int a,int b){
	Intt();
	int x=a,y=b;bool op=1;
	if(dep[x]<dep[y]){
		swap(x,y);op=0;
	}

	for(int i=16;i>=0;i--){
		if(dep[fa[i][x]]>=dep[y]){
			if(op) Update(1,i,x);
			else Update(0,i,x);
			x=fa[i][x];
		}
	}

	for(int i=16;i>=0;i--){
		if(fa[i][x]!=fa[i][y]){
			if(op){
				Update(1,i,x);
				Update(0,i,y);
			}
			else{
				Update(1,i,y);
				Update(0,i,x);
			}
			x=fa[i][x];y=fa[i][y];
		}
	}

	if(x!=y){
		if(op){
			Update(1,0,x);
			Update(0,0,y);
		}
		else{
			Update(1,0,y);
			Update(0,0,x);
		}
	}
	MaxUD=maxx-minn;

	// printf("ud:%d u:%d d:%d\n",MaxUD,MaxU,MaxD);

	return Max(MaxUD,Max(MaxU,MaxD));
}

int main(){
	// freopen("my.in","r",stdin);
	// freopen("my.out","w",stdout);

    read(n);
    lg2[0]=-1;for(int i=1;i<=n;i++) lg2[i]=lg2[i>>1]+1;
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=n-1;i++){
        int from,to;read(from);read(to);
        add(from,to);add(to,from);
    }

    dfs(1,0);

    read(q);
    for(int i=1;i<=q;i++){
        int a,b;read(a);read(b);
        int ans=solve(a,b);
        printf("%d\n",ans);
    }
    return 0;
}

2.4 HDU 6135

这个题一眼看上去没有什么思路,但实际上不难发现一个性质:所有位置上的值增大的数是约是 \(n\log n\)​ 级别的。这是因为调和级数约是 \(n\log n\) ,而所有位置的值增大的极限是:

\[\sum\limits_{i=1}^n\lfloor \frac{m}{i} \rfloor \]

这里 \(n,m\) 同级,由此可以证明。

所以我们直接暴力去做这个事情,我们用线段树去维护每一个位置再加多少次就该增大了。

不难发现,修改所有位置的总次数是 \(n\log n\) ,由于修改一个位置的复杂度为 \(O(\log n)\) ,所以我们的总复杂度是 \(n\log^2 n\)

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define int long long 
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
};

template<typename T> T Min(T a,T b){
    return a<b?a:b;
}

int n,q,a[N];

struct node{
    int sum,val,minn,lazy,len;
    inline node() {}
};

struct DS{
    node p[N<<2];
    inline void Clear(int k){
        p[k].sum=p[k].val=p[k].minn=p[k].lazy=p[k].len=0;
    }
    inline void PushUp(int k){
        p[k].len=p[k*2].len+p[k*2+1].len;
        p[k].sum=p[k*2].sum+p[k*2+1].sum;
        p[k].val=p[k*2].val+p[k*2+1].val;
        p[k].minn=Min(p[k*2].minn,p[k*2+1].minn);
    }
    inline void Build(int k,int l,int r){
        if(l==r){
            p[k].sum=0;p[k].val=a[l];p[k].minn=a[l];p[k].len=1;
            return;
        }
        Clear(k);
        int mid=(l+r)>>1;
        Build(k*2,l,mid);Build(k*2+1,mid+1,r);
        PushUp(k);
    }
    inline void A(int k,int x){
        p[k].val+=p[k].len*x;
        p[k].minn+=x;p[k].lazy+=x;
    }
    inline void PushDown(int k){
        A(k*2,p[k].lazy);A(k*2+1,p[k].lazy);
        p[k].lazy=0;
    }
    inline void Insert(int k,int l,int r,int z,int y,int x){
        if(l==z&&r==y){
            A(k,x);
            if(p[k].minn==0) Change(k,l,r);
            return;
        }
        int mid=(l+r)>>1;
        if(p[k].lazy) PushDown(k);
        if(y<=mid) Insert(k*2,l,mid,z,y,x);
        else if(z>mid) Insert(k*2+1,mid+1,r,z,y,x);
        else Insert(k*2,l,mid,z,mid,x),Insert(k*2+1,mid+1,r,mid+1,y,x);
        PushUp(k);
    }
    inline int AskSum(int k,int l,int r,int z,int y){
        if(l==z&&r==y) return p[k].sum;
        int mid=(l+r)>>1;
        if(p[k].lazy) PushDown(k);
        if(y<=mid) return AskSum(k*2,l,mid,z,y);
        else if(z>mid) return AskSum(k*2+1,mid+1,r,z,y);
        else return AskSum(k*2,l,mid,z,mid)+AskSum(k*2+1,mid+1,r,mid+1,y);
    }
    inline void Change(int k,int l,int r){
        if(l==r){
            p[k].sum++;p[k].val=a[l];p[k].minn=a[l];
            return;
        }
        int mid=(l+r)>>1;
        if(p[k].lazy) PushDown(k);
        if(p[k*2].minn==0) Change(k*2,l,mid);
        if(p[k*2+1].minn==0) Change(k*2+1,mid+1,r);
        PushUp(k);
    }
};
DS ds;

string s;

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    while(scanf("%d %d\n",&n,&q)!=EOF){
        for(int i=1;i<=n;i++) read(a[i]);
        ds.Build(1,1,n);
        for(int i=1;i<=q;i++){
            cin>>s;
            int a,b;read(a);read(b);
            if(s=="add") ds.Insert(1,1,n,a,b,-1);
            else{
                int ans=ds.AskSum(1,1,n,a,b);
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

2.5 HDU 5634

有一个结论:当 \(x\ge 2\)​ 时, \(\phi(x)\)​ 都为偶数,这个用 \(\phi(x)\)​ 通项公式不难证明。

所以,对于一个 \(\phi(x)\) 来说,用最多 \(\log x\) 次就可以变成 \(1\) 。这是一个很松很松的上界。

所以我们可以直接暴力的去求 \(\phi\) 这个复杂度远远达不到 \(O(n\log 10^{7})\)

同时,对于第三个操作,我们可以进行打标记。对于数字一样的区间,求 \(\phi\) 的时候我们做一次打标记就可以了。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 300010
#define M 700010
using namespace std;

const int INF=0x3f3f3f3f;
const int MAXN=1e7+10;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int n,m,a[N];

struct node{
    ll val;
    int lazy,maxx,minn,len;
    bool op;
    node(){op=0;}
};

bool NotPrime[MAXN];
int prime[M],tail,Phi[MAXN];

inline void GetPhi(){
    NotPrime[1]=1;Phi[1]=1;
    for(int i=2;i<=1e7;i++){
        if(!NotPrime[i]){prime[++tail]=i;Phi[i]=i-1;}
        for(int j=1;j<=tail&&i*prime[j]<=1e7;j++){
            int k=i*prime[j];
            NotPrime[k]=1;
            if(i%prime[j]==0){
                Phi[k]=Phi[i]*prime[j];
                break;
            }
            else Phi[k]=Phi[i]*Phi[prime[j]];
        }
    }
    // for(int i=1;i<=10;i++) printf("i:%d Phi:%d\n",i,Phi[i]);
}

struct DS{
    node p[N<<2];
    inline void Pushup(int k){
        p[k].val=p[k*2].val+p[k*2+1].val;
        p[k].len=p[k*2].len+p[k*2+1].len;
        p[k].maxx=max(p[k*2].maxx,p[k*2+1].maxx);
        p[k].minn=min(p[k*2].minn,p[k*2+1].minn);
    }
    inline void ClearNode(int k){
        p[k].val=p[k].lazy=p[k].minn=p[k].maxx=p[k].op=p[k].len=0;
    }
    inline void Build(int k,int l,int r){
        if(l==r){
            p[k].val=a[l];p[k].minn=p[k].maxx=a[l];p[k].len=1;
            return;
        }
        ClearNode(k);
        int mid=(l+r)>>1;
        Build(k*2,l,mid);Build(k*2+1,mid+1,r);
        Pushup(k);
    }
    inline void A(int k,ll x){
        p[k].val=x*(ll)p[k].len;p[k].lazy=x;p[k].maxx=p[k].minn=x;
        p[k].op=1;
    }
    inline void Pushdown(int k){
        A(k*2,p[k].lazy);A(k*2+1,p[k].lazy);
        p[k].lazy=0;p[k].op=0;
    }
    inline void ChangeInt(int k,int l,int r,int z,int y,int x){
        if(l==z&&r==y){
            A(k,x);return;
        }
        if(p[k].op) Pushdown(k);
        int mid=(l+r)>>1;
        if(y<=mid) ChangeInt(k*2,l,mid,z,y,x);
        else if(z>mid) ChangeInt(k*2+1,mid+1,r,z,y,x);
        else ChangeInt(k*2,l,mid,z,mid,x),ChangeInt(k*2+1,mid+1,r,mid+1,y,x);
        Pushup(k);
    }
    inline void ChangePhi(int k,int l,int r,int z,int y){
        if(p[k].maxx==p[k].minn&&l==z&&r==y){
            int phi=Phi[p[k].minn];
            A(k,phi);return;
        }
        if(p[k].op) Pushdown(k);
        int mid=(l+r)>>1;
        if(y<=mid) ChangePhi(k*2,l,mid,z,y);
        else if(z>mid) ChangePhi(k*2+1,mid+1,r,z,y);
        else ChangePhi(k*2,l,mid,z,mid),ChangePhi(k*2+1,mid+1,r,mid+1,y);
        Pushup(k);
    }
    inline ll AskSum(int k,int l,int r,int z,int y){
        if(l==z&&r==y) return p[k].val;
        if(p[k].op) Pushdown(k);
        int mid=(l+r)>>1;
        if(y<=mid) return AskSum(k*2,l,mid,z,y);
        else if(z>mid) return AskSum(k*2+1,mid+1,r,z,y);
        else return AskSum(k*2,l,mid,z,mid)+AskSum(k*2+1,mid+1,r,mid+1,y);
    }
};
DS ds;

int t;

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    GetPhi();
    while(t--){
        read(n);read(m);
        for(int i=1;i<=n;i++) read(a[i]);
        ds.Build(1,1,n);
        for(int i=1;i<=m;i++){
            int op,l,r;
            read(op);read(l);read(r);
            if(op==1){
                ds.ChangePhi(1,1,n,l,r);
            }
            else if(op==2){
                int x;read(x);
                ds.ChangeInt(1,1,n,l,r,x);
            }
            else{
                ll ans=ds.AskSum(1,1,n,l,r);
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

2.6 codeforces 1149 D

这个题卡了我较长时间。

因为需要支持区间修改,无法用离线算法维护,所以我们考虑用线段树在线维护。

用线段树维护的难点在于合并信息。

首先我们这样用括号序列构造这棵树:遇到左括号往儿子走,右括号往父亲走,所以,树的直径在线段树上就是这样的:

\[\text{))))))((((((((} \]

我们考虑一个区间中的最大直径,首先与左右两边最大区间求最值,这个不用说,我们考虑如何合并两段。

我们设参与合并的左区间的左括号数量为 \(a\) ,右括号数量为 \(b\) ,参与合并的右区间的左括号数量为 \(c\)​ 右括号数量为 \(d\) ,那么我们有答案为:

\[b+|a-d|+c \]

之所以是这个答案,是因为左边右边组成的区间同样也是形如上面的括号序列,这是因为任意一个区间,把能消除的都消除,就会变成上面那个式子。

我们把绝对值拆开:

\[\max(b+a-d+c,b-a+d+c) \]

不难发现如果我们单独维护 \(a,b,c,d\) 是比较难维护的,所以我们考虑维护 \(a+b,b-a,c-d,d+c\)

怎么维护?我们先考虑这些值的意义,不难发现这些意义分别是:

  • \(a+b\) 后缀左括号加右括号最大值。
  • \(b-a\) 后缀右括号减左括号最大值。
  • \(c-d\)​ 前缀左括号减右括号最大值。
  • \(d+c\) 前缀左括号加右括号最大值。

注意,上面的所有意义都是在合并了一些之后,形如 \()))))((((((\) 的左右括号最值。

我们考虑如何维护上面的值,以维护第一个举例,设其为 \(x\)

首先我们考虑这个后缀的开始位置。如果开始位置在右区间,显然值应该是右区间的 \(x\)

如果开始位置在左区间,首先,这个一定包含了右区间,设右区间全部合并左括号数量为 \(l\) ,右括号数量为 \(r\) ,左区间部分的左括号为 \(a\) ,右括号为 \(b\) ,那么答案为 \(b+|a-r|+l\) ,我们再次分类讨论:

\[\max(b+a-r+l,b-a+r-l) \]

不难发现最大化的 \(a+b,b-a\) 是我们要维护的值。而 \(l,r\) 也好维护。

所以我们就可以用线段树做了:代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 300010
#define M 700010
using namespace std;

const int INF=0x3f3f3f3f;
const int MAXN=1e7+10;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int n,m,a[N];

struct node{
    ll val;
    int lazy,maxx,minn,len;
    bool op;
    node(){op=0;}
};

bool NotPrime[MAXN];
int prime[M],tail,Phi[MAXN];

inline void GetPhi(){
    NotPrime[1]=1;Phi[1]=1;
    for(int i=2;i<=1e7;i++){
        if(!NotPrime[i]){prime[++tail]=i;Phi[i]=i-1;}
        for(int j=1;j<=tail&&i*prime[j]<=1e7;j++){
            int k=i*prime[j];
            NotPrime[k]=1;
            if(i%prime[j]==0){
                Phi[k]=Phi[i]*prime[j];
                break;
            }
            else Phi[k]=Phi[i]*Phi[prime[j]];
        }
    }
    // for(int i=1;i<=10;i++) printf("i:%d Phi:%d\n",i,Phi[i]);
}

struct DS{
    node p[N<<2];
    inline void Pushup(int k){
        p[k].val=p[k*2].val+p[k*2+1].val;
        p[k].len=p[k*2].len+p[k*2+1].len;
        p[k].maxx=max(p[k*2].maxx,p[k*2+1].maxx);
        p[k].minn=min(p[k*2].minn,p[k*2+1].minn);
    }
    inline void ClearNode(int k){
        p[k].val=p[k].lazy=p[k].minn=p[k].maxx=p[k].op=p[k].len=0;
    }
    inline void Build(int k,int l,int r){
        if(l==r){
            p[k].val=a[l];p[k].minn=p[k].maxx=a[l];p[k].len=1;
            return;
        }
        ClearNode(k);
        int mid=(l+r)>>1;
        Build(k*2,l,mid);Build(k*2+1,mid+1,r);
        Pushup(k);
    }
    inline void A(int k,ll x){
        p[k].val=x*(ll)p[k].len;p[k].lazy=x;p[k].maxx=p[k].minn=x;
        p[k].op=1;
    }
    inline void Pushdown(int k){
        A(k*2,p[k].lazy);A(k*2+1,p[k].lazy);
        p[k].lazy=0;p[k].op=0;
    }
    inline void ChangeInt(int k,int l,int r,int z,int y,int x){
        if(l==z&&r==y){
            A(k,x);return;
        }
        if(p[k].op) Pushdown(k);
        int mid=(l+r)>>1;
        if(y<=mid) ChangeInt(k*2,l,mid,z,y,x);
        else if(z>mid) ChangeInt(k*2+1,mid+1,r,z,y,x);
        else ChangeInt(k*2,l,mid,z,mid,x),ChangeInt(k*2+1,mid+1,r,mid+1,y,x);
        Pushup(k);
    }
    inline void ChangePhi(int k,int l,int r,int z,int y){
        if(p[k].maxx==p[k].minn&&l==z&&r==y){
            int phi=Phi[p[k].minn];
            A(k,phi);return;
        }
        if(p[k].op) Pushdown(k);
        int mid=(l+r)>>1;
        if(y<=mid) ChangePhi(k*2,l,mid,z,y);
        else if(z>mid) ChangePhi(k*2+1,mid+1,r,z,y);
        else ChangePhi(k*2,l,mid,z,mid),ChangePhi(k*2+1,mid+1,r,mid+1,y);
        Pushup(k);
    }
    inline ll AskSum(int k,int l,int r,int z,int y){
        if(l==z&&r==y) return p[k].val;
        if(p[k].op) Pushdown(k);
        int mid=(l+r)>>1;
        if(y<=mid) return AskSum(k*2,l,mid,z,y);
        else if(z>mid) return AskSum(k*2+1,mid+1,r,z,y);
        else return AskSum(k*2,l,mid,z,mid)+AskSum(k*2+1,mid+1,r,mid+1,y);
    }
};
DS ds;

int t;

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    GetPhi();
    while(t--){
        read(n);read(m);
        for(int i=1;i<=n;i++) read(a[i]);
        ds.Build(1,1,n);
        for(int i=1;i<=m;i++){
            int op,l,r;
            read(op);read(l);read(r);
            if(op==1){
                ds.ChangePhi(1,1,n,l,r);
            }
            else if(op==2){
                int x;read(x);
                ds.ChangeInt(1,1,n,l,r,x);
            }
            else{
                ll ans=ds.AskSum(1,1,n,l,r);
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}
posted @ 2021-07-31 13:14  hyl天梦  阅读(36)  评论(0编辑  收藏  举报