线段树题集--SPOJ GSS1~GSS8解题报告

当前进度:20201029 更新至GSS5

GSS1 - Can you answer these queries I

第一题,主要处理不同区间里,最大值,从左边开始的最大值,从右边开始的最大值,总和 这4个点的合并即可,luogu之前做过和这题思路及其类似的就是我,这题也是线段树的题,不过侧重于维护区间连续1的长度,并且需要支持区间修改和区间查询,不过大概思路是可以借鉴的
那么回到本题,既然需要是查询某一段中最大的前缀和,那么就得想如何处理区间合并,假设现在左子区间和右子区间已经确定了最大前缀和,那这一段的最大前缀和,要么是左子区间的最大前缀和,要么是右子区间的最大前缀和,或者,第三种可能,左子区间取右边的一部分,右子区间取左边的一部分,这2部分重新组合,变成当前区间的最大前缀和,所以我们合并2个区间的区间的时候,需要知道三个值,当前最大,当前以左边界为起点的最大,当前以右边界为终点的最大,维护好这三个值即可

总结:一开始就想到思路了,但是写的时候出了点问题,我额外记录了maxsum,lmaxsum,rmaxsum各自的左边界、右边界,但是其实压根不需要(
精简后的代码版本如下

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
typedef vector<int> VI;
const ll mod=1000000007;
const int maxn=1e6+10;
//const int inf=0x3f3f3f3f;
const ll inf = 1e18;
//const int inf=0x7fffffff;
ll gcd(ll a,ll b) {
    return b?gcd(b,a%b):a;
}
//	  freopen("1.txt","r",stdin);
//    freopen("2.txt","w",stdout);
#define ms(a) memset(a,0,sizeof(a))
#define mss(a) memset(a,-1,sizeof(a))
#define msi(a) memset(a,inf,sizeof(a))
#define iossync ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
ll a[maxn];
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
struct Segment_Tree{
    struct node{
        ll sum;
        ll maxsum;;
        ll lmaxsum;
        ll rmaxsum;
        node (){
        	sum=maxsum=lmaxsum=rmaxsum=0;
		}
        inline node operator + (const node & b) const
        {
            node c;
			c.sum=sum+b.sum;
        	c.maxsum=max(rmaxsum+b.lmaxsum,max( maxsum,b.maxsum));
  	 	 	c.lmaxsum=max(lmaxsum,sum+b.lmaxsum);
			c.rmaxsum=max(b.rmaxsum,b.sum+rmaxsum);
        return c;
        } 
    }t[maxn<<2];
    node now;
    node query_maxsum(int p,int l,int r,int ql,int qr)
    {
        if(ql<=l && r<=qr)
        {
            return t[p];
        }
        int mid=(l+r)>>1;
        if(ql<=mid && mid<qr)
        {
            return query_maxsum(ls(p),l,mid,ql,qr)+query_maxsum(rs(p),mid+1,r,ql,qr);
        }
        else
        {
            if(ql<=mid)
            {
                return query_maxsum(ls(p),l,mid,ql,qr);
            }
            else
            {
                return query_maxsum(rs(p),mid+1,r,ql,qr);
            }
        }
    }
    void build(int p,int l,int r)
    {
        if(l==r)
        {
            t[p].maxsum=t[p].lmaxsum=t[p].rmaxsum=a[l];
            t[p].sum=a[l];
            return ;
        }
        int mid=(l+r)>>1;
        build(ls(p),l,mid);
        build(rs(p),mid+1,r);
        // push_up(p);
        t[p]=t[ls(p)]+t[rs(p)];
    }
}S1;
int n,m;
int l,r;
int main()
{
    cin>>n;
    rep(i,1,n+1)
    {
        cin>>a[i];
    }
    cin>>m;
    S1.build(1,1,n);
    while(m--)
    {
        cin>>l>>r;
        S1.now=S1.query_maxsum(1,1,n,l,r);
        cout<<S1.now.maxsum<<"\n";
    }
    return 0;
}







GSS2 - Can you answer these queries II

第二题,给出n个数字,q次询问,求最大子段和,而相同的数字只算一次。

我们可以注意到,单独查看当前询问时,范围为 [ l , r ] [l,r] [l,r],也就是a[r]之后的数字压根不影响当前查询,所以我们可以离线处理,将查询以 r r r为标准,从小到大排序,每次查询前,假如当前 a [ r ] a[r] a[r]及之前的未加入线段树,就先加入线段树,而a[r]也可以进行处理,既然相同数字只算一次,我们在读入的时候预设一个 l a s t last last数组,用来储存每个数字上一次出现的位置 l a s t [ a [ i ] ] last[a[i]] last[a[i]],这样的话,每次 a [ i ] a[i] a[i]加入线段树的时候,其实只需要修改 [ l a s t [ a [ i ] + 1 , i ] [last[a[i]+1,i] [last[a[i]+1,i]这一段就好了
那么查询和线段树的插入元素已经想好了,那么思考线段树的架构了。
既然我们的每个 a [ i ] a[i] a[i]加入的时候只改变 [ l a s t [ a [ i ] + 1 , i ] [last[a[i]+1,i] [last[a[i]+1,i],那我们可以构造线段树的当前sum为 [ l , r ] [l,r] [l,r]的和,其中l永远不变,r随当前查询更改,即每个节点维护以l为开头,以当前 q u e r y query query r r r为结尾的不含重复数字的 s u m sum sum,所以每次查询的时候直接查询 m a x max max就好,但这个 m a x max max应该是历史最大值,所以我们的线段树应该包含4个值, s u m sum sum:当前 [ l , q u e r y [ r ] ] [l,query[r]] [l,query[r]]的总和, h i s m a x hismax hismax:历史 [ l , q u e r y [ r ] ] [l,query[r]] [l,query[r]]的最大总和, s u m sum sum h i s m a x hismax hismax各自需要一个 l a z y lazy lazy标记来简化修改,具体可以见代码
这题数据是 − 1 e 5   1 e 5 -1e5~1e5 1e5 1e5,所以用last数组来存储上一次出现的位置的时候,记得加上一个值,把 − 1 e 5 -1e5 1e5调成非负数

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
typedef vector<int> VI;
const ll mod=1000000007;
const int maxn=1e6+10;
//const int inf=0x3f3f3f3f;
const ll inf = 1e18;
//const int inf=0x7fffffff;
ll gcd(ll a,ll b) {
    return b?gcd(b,a%b):a;
}
//	  freopen("1.txt","r",stdin);
//    freopen("2.txt","w",stdout);
#define ms(a) memset(a,0,sizeof(a))
#define mss(a) memset(a,-1,sizeof(a))
#define msi(a) memset(a,inf,sizeof(a))
#define iossync ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head
ll a[maxn];
int pre[maxn];
int last[maxn];
int n,m;
struct Segment_Tree{
    #define ls(p) (p<<1)
    #define rs(p) (p<<1|1)
    struct node{
        ll sum,hismax,sumlazy,hislazy;
        node(){
            sum=hismax=sumlazy=hislazy=0;
        }
        void init_lazy()
        {
            sumlazy=hislazy=0;
        }
        void init_all()
        {
            sum=hismax=sumlazy=hislazy=0;
        }
        node operator + (const node & b) const{
            node ans;
            ans.sum=max(sum,b.sum);
            ans.hismax=max(hismax,b.hismax);
            return ans;
        }
        node operator + (const ll & b) const{
            node ans;
            ans.sum=sum+b;
            ans.hismax=max(hismax,ans.sum);
            ans.sumlazy=sumlazy+b;
            ans.hislazy=max(hislazy,ans.sumlazy);
            return ans;
        }
    }t[maxn<<2];
    void push_down(int p)
    {
//    	cout<<p<<"\n";
        t[ls(p)].hismax=max(t[ls(p)].hismax,t[ls(p)].sum+t[p].hislazy);
        t[rs(p)].hismax=max(t[rs(p)].hismax,t[rs(p)].sum+t[p].hislazy);
        t[ls(p)].sum+=t[p].sumlazy;
        t[rs(p)].sum+=t[p].sumlazy;
        t[ls(p)].hislazy=max(t[ls(p)].hislazy,\
        t[ls(p)].sumlazy+t[p].hislazy);
        t[rs(p)].hislazy=max(t[rs(p)].hislazy,\
        t[rs(p)].sumlazy+t[p].hislazy);
        t[ls(p)].sumlazy+=t[p].sumlazy;
        t[rs(p)].sumlazy+=t[p].sumlazy;
        t[p].init_lazy();
    }
    void push_up(int p)
    {
        t[p]=t[ls(p)]+t[rs(p)];
    }
    void change_add(int p,int l,int r,int ql,int qr,ll k)
    {
        if(ql<=l && r<=qr)
        {
            t[p]=t[p]+k;
            return ;
        }
        push_down(p);
        int mid=(l+r)>>1;
        if(ql<=mid)
        change_add(ls(p),l,mid,ql,qr,k);
        if(mid<qr)
        change_add(rs(p),mid+1,r,ql,qr,k);
        push_up(p);
    }
    ll query_hismax(int p,int l,int r,int ql,int qr)
    {
        if(ql<=l && r<=qr)
        {
            return t[p].hismax;
        }
        push_down(p);
        int mid=(l+r)>>1;
        ll ans=0;
        if(ql<=mid)
        ans=max(ans,query_hismax(ls(p),l,mid,ql,qr));
        if(mid<qr)
        ans=max(ans,query_hismax(rs(p),mid+1,r,ql,qr));
        return ans;
    }
    #undef ls
    #undef rs
}S1;
struct NODE{
    int l,r,no;
    bool operator < (const NODE & b) const{
        return r<b.r;
    } 
}b[maxn];
ll c[maxn];
int main()
{
    cin>>n;
    rep(i,1,n+1)
    {
        cin>>a[i];
        //-100000 ~~ 100000
        pre[i]=last[a[i]+(int ) 1e5 +10];
        last[a[i]+(int ) 1e5+10]=i;
    }
    cin>>m;
    rep(i,1,m+1)
    {
        cin>>b[i].l>>b[i].r;
        b[i].no=i;
    }
    sort(b+1,b+m+1);
    int first=1;
    rep(i,1,n+1)
    {//按顺序进行添加
        S1.change_add(1,1,n,pre[i]+1,i,a[i]);
        for(;first<=m && b[first].r<=i;first++)
        {
            c[b[first].no]=S1.query_hismax(1,1,n,b[first].l,b[first].r);
        }
    }
    rep(i,1,m+1)
    {
        cout<<c[i]<<"\n";
    }
    return 0;
}






GSS3 - Can you answer these queries III

这题做的挺尴尬的,可能是因为刚做完2,思路完全走2的思路,想着继续先离线,按r进行,将查询和修改分开操作,然后敲了200多行(逃),半成品在这,以后说不定可以改成完全?

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
typedef vector<int> VI;
const ll mod=1000000007;
const int maxn=1e6+10;
//const int inf=0x3f3f3f3f;
const ll inf = 1e18;
//const int inf=0x7fffffff;
ll gcd(ll a,ll b) {
    return b?gcd(b,a%b):a;
}
//	  freopen("1.txt","r",stdin);
//    freopen("2.txt","w",stdout);
#define ms(a) memset(a,0,sizeof(a))
#define mss(a) memset(a,-1,sizeof(a))
#define msi(a) memset(a,inf,sizeof(a))
#define iossync ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head
// n  5e4
// a[i] -1e4~1e4
int a[maxn];
//究极离线操作
//查询按R从小到大排序
//每个节点维护的就是[l,query[r]]的值
//每次R往右边扩的时候,[1,r]全部加上a[r]
//修改 ai -> y
// 线段树的[1,i] + y-ai 
struct Segment_Tree{
	#define ls(p) (p<<1)
	#define rs(p) (p<<1|1)
	struct node{
		int sum;
		int hismax;
		int sumlazy;
		int hislazy; 
		node()
		{
			sum=hismax=0;
			sumlazy=hislazy=0;
		}
		void init_lazy()
		{
			sumlazy=hislazy=0;
		}
		void init_all()
		{
			sum=hismax=0;
			sumlazy=hislazy=0;
		}
		node operator + (const node & b) const{
            node ans;
            ans.sum=max(sum,b.sum);
            ans.hismax=max(hismax,b.hismax);
            return ans;
        }
		node operator + (const ll & b)
		{
			node ans;
			ans.sum=sum+b;
            ans.hismax=max(hismax,ans.sum);
            ans.sumlazy=sumlazy+b;
            ans.hislazy=max(hislazy,ans.sumlazy);
            return ans;
		}
	}t[maxn<<2];
	void push_down(int p)
	{
		t[ls(p)].hismax=max(t[ls(p)].hismax,t[ls(p)].sum+t[p].hislazy);
        t[rs(p)].hismax=max(t[rs(p)].hismax,t[rs(p)].sum+t[p].hislazy);
        t[ls(p)].sum+=t[p].sumlazy;
        t[rs(p)].sum+=t[p].sumlazy;
        t[ls(p)].hislazy=max(t[ls(p)].hislazy,\
        t[ls(p)].sumlazy+t[p].hislazy);
        t[rs(p)].hislazy=max(t[rs(p)].hislazy,\
        t[rs(p)].sumlazy+t[p].hislazy);
        t[ls(p)].sumlazy+=t[p].sumlazy;
        t[rs(p)].sumlazy+=t[p].sumlazy;
        t[p].init_lazy();
	}
	void push_up(int p)
	{
		t[p]=t[ls(p)]+t[rs(p)];	
	}
	void build(int p,int l,int r)
	{
		if(l==r)
		{
			t[p].sum=t[p].hismax=a[l];
			t[p].init_lazy();
			return ;
		}
		int mid=(l+r)>>1;
		build(ls(p),l,mid);
		build(rs(p),mid+1,r);
		push_up(p);
	}
	void change(int p,int l,int r,int ql,int qr,int k)
	{
		if(ql<=l && r<=qr)
		{
			t[p]=t[p]+k;
			return ;
		}
		push_down(p);
		int mid=(l+r)>>1;
		if(ql<=mid)
		change(ls(p),l,mid,ql,qr,k);
		if(mid<qr)
		change(rs(p),mid+1,r,ql,qr,k);
		push_up(p);
	}
	int query(int p,int l,int r,int ql,int qr)
	{
		if(ql<=l && r<=qr)
		{
			return t[p].hismax;
		}
		push_down(p);
		int mid=(l+r)>>1;
		int ans=0;
		if(ql<=mid)
		ans=max(ans,query(ls(p),l,mid,ql,qr));
		if(mid<qr)
		ans=max(ans,query(rs(p),mid+1,r,ql,qr));
		return ans;
	}
	#undef ls
	#undef rs
}S1;
struct NODE{
	int id,l,r;
//	int id;
	bool operator < (const NODE & b) const{
		if(r==b.r)
		return id<b.id;
        return r<b.r;
    }
}b[maxn],c[maxn],d[maxn];
int e[maxn];
//int d[maxn];
int n,m,qwe;
int main()
{
	cin>>n;
	rep(i,1,n+1)
	{
		cin>>a[i];
	}
//	S1.build(1,1,n);
	cin>>m;
	int m1,m2;
	m1=m2=0;
	rep(i,1,m+1)
	{
//		cin>>b[i].qwe>>b[i].l>>b[i].r;
		cin>>qwe;
		if(qwe)
		{//query
			b[++m1].id=i;
			cin>>b[m1].l>>b[m1].r;
		}
		else
		{
			c[++m2].id=i;
			cin>>c[m2].l>>c[m2].r;
		}
	}
	sort(b+1,b+m1+1);
	int cnt2=1;//标记修改数组跑到哪了
	int first=1;//标记当前数组添加几个进入线段树了 
	rep(i,1,m1+1)
	{
		while(cnt2<=m2 && c[cnt2].id<b[i].id)
		{
			if(b[i].r<c[cnt2].l)
			{//还没查询到这点呢,直接改就行 
				a[c[cnt2].l]=c[cnt2].r;
//				cnt2++;
			}
			else
			{//已经查询过了,那这个时候就得对之前的都做一次修改喽 
				S1.change(1,1,n,1,c[cnt2].l,c[cnt2].r-a[c[cnt2].l]);
				a[c[i].l]=c[i].r;
				//同时别忘了哦,记得修改a[c[cnt].l],方便后续继续修改 
			}
//			S1.change(1,1,n,1,c[cnt2].l,c[cnt2].r-a[c[cnt2].l]);
//			a[c[i].l]=c[i].r;
			cnt2++;
		}
		while(first<=b[i].r)
		{
			
		}
		d[i].id=i;
		d[i].l=S1.query(1,1,n,b[i].l,b[i].r);
		d[i].r=0;
//		cout<<query(1,1,n,b[i].l,b[i].r)<<"\n";
	}
	sort(d+1,d+m1+1);
	rep(i,1,m+1)
	{
		cout<<d[i].l<<"\n";
	}
	return 0;
}








但其实这题特别简单,就是把GSS1的代码加个修改,呜呜呜,代码如下(int就可,不用longlong)

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
typedef vector<int> VI;
const ll mod=1000000007;
const int maxn=1e6+10;
//const int inf=0x3f3f3f3f;
const ll inf = 1e18;
//const int inf=0x7fffffff;
ll gcd(ll a,ll b) {
    return b?gcd(b,a%b):a;
}
//	  freopen("1.txt","r",stdin);
//    freopen("2.txt","w",stdout);
#define ms(a) memset(a,0,sizeof(a))
#define mss(a) memset(a,-1,sizeof(a))
#define msi(a) memset(a,inf,sizeof(a))
#define iossync ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head
// n  5e4
// a[i] -1e4~1e4
#define int ll
int a[maxn];
int n,m,qwe,l,r,k;
struct Segment_Tree{
	#define ls(p) (p<<1)
	#define rs(p) (p<<1|1)
	struct node{
		int sum,lmax,rmax,mx;
		node()
		{
			sum=lmax=rmax=mx=0;
		}
		node operator +  (const node & b ) const
		{
			node ans;
			ans.sum=sum+b.sum;
			ans.lmax=max(lmax,sum+b.lmax);
			ans.rmax=max(b.rmax,rmax+b.sum);
//			ans.mx=max(max(mx,b.mx),max(ans.lmax,ans.rmax));
			ans.mx=max(max(mx,b.mx),rmax+b.lmax);
			return ans;
		}
	}t[maxn<<2];
	void push_up(int p)
	{
		t[p]=t[ls(p)]+t[rs(p)];
	}
	void build(int p,int l,int r)
	{
		if(l==r)
		{
			t[p].sum=t[p].lmax=t[p].rmax=t[p].mx=a[l];
			return ;
		}
		int mid=(l+r)>>1;
		build(ls(p),l,mid);
		build(rs(p),mid+1,r);
		push_up(p);
	}
	void change(int p,int l,int r,int ql,int k)
	{
		if(l==r)
		{
			t[p].sum=t[p].lmax=t[p].rmax=t[p].mx=k;
			return ;
		}
		int mid=(l+r)>>1;
		if(ql<=mid)
		change(ls(p),l,mid,ql,k);
		if(mid<ql)
		change(rs(p),mid+1,r,ql,k);
		push_up(p);
	}
	node query(int p,int l,int r,int ql,int qr)
	{
		if(ql<=l && r<=qr)
		{
			return t[p];
		}
		int mid=(l+r)>>1;
		if(ql<=mid && mid<qr)
		{
			return query(ls(p),l,mid,ql,qr)+query(rs(p),mid+1,r,ql,qr);
		}
		else
		if(ql<=mid)
		{
			return query(ls(p),l,mid,ql,qr);
		}
		else
		{
			return query(rs(p),mid+1,r,ql,qr);
		}
	}
}S1;
#undef int
int main()
{
	iossync
	cin>>n;
	rep(i,1,n+1)
	{
		cin>>a[i];
	}
	S1.build(1,1,n);
	cin>>m;
	while(m--)
	{
		cin>>qwe;
		switch(qwe)
		{
			case 0:{
				cin>>l>>k;
				S1.change(1,1,n,l,k);
				break;
			}
			case 1:{
				cin>>l>>r;
				cout<<S1.query(1,1,n,l,r).mx<<"\n";
				break;
			}
		}
	}
	return 0;
}





GSS4 - Can you answer these queries IV

这题只需要2个操作,求和是一样的,但更改操作跟以往完全不一样,是将区域内数字开方(向下取整)。
这题初看嘛,毫无思路,开方和,总之我想不到有什么简便算法可以一步把和转换成开方后的和。。。。
那这题似乎只有暴力修改一种途径了?每次将每个区域的元素重新计算吗?好像只能这样了,但注意到,我们每次是开方,而1的开方仍然是1,0也是,所以当一个数字到达1或0的时候,再进行开方操作已经毫无意义了,所以我们可以统计一个区域的max,假如当前max是1,就代表当前区域内数字不是1就是0了,开方已经毫无意义了,这个区域就不用操作了,而这题的每个元素最大只到1e18,最多开方6次,也已经到达1了,所以每个元素的最多操作次数仅为6,那么这题就做完了

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
typedef vector<int> VI;
const ll mod=1000000007;
const int maxn=1e6+10;
//const int inf=0x3f3f3f3f;
const ll inf = 1e18;
//const int inf=0x7fffffff;
ll gcd(ll a,ll b) {
    return b?gcd(b,a%b):a;
}
//	  freopen("1.txt","r",stdin);
//    freopen("2.txt","w",stdout);
#define ms(a) memset(a,0,sizeof(a))
#define mss(a) memset(a,-1,sizeof(a))
#define msi(a) memset(a,inf,sizeof(a))
#define iossync ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head

ll a[maxn];
int n,m,qwe,l,r;
struct Segment_Tree{
	#define ls(p) (p<<1)
	#define rs(p) (p<<1|1)
	struct node{
		ll mx;
//		ll val;
		ll sum;
		node()
		{
			mx=sum=0;
		}
	}t[maxn<<2];
	void push_up(int p)
	{
		t[p].mx=max(t[ls(p)].mx,t[rs(p)].mx);
		t[p].sum=t[ls(p)].sum+t[rs(p)].sum;
	}
	void build(int p,int l,int r)
	{
		if(l==r)
		{
			t[p].mx=t[p].sum=a[l];
			return ; 
		}
		int mid=(l+r)>>1;
		build(ls(p),l,mid);
		build(rs(p),mid+1,r);
		push_up(p);
	}
	void change(int p,int l,int r,int ql,int qr)
	{
		if(ql<=l && r<=qr && t[p].mx<=1)
		{
			return ;
		}
		if(l==r)
		{
//			cout<<ceil(sqrt(t[p].sum))<<"\n";
			t[p].mx=t[p].sum=floor(sqrt(t[p].sum));
			return ;
		}
		int mid=(l+r)>>1;
		if(ql<=mid)
		change(ls(p),l,mid,ql,qr);
		if(mid<qr)
		change(rs(p),mid+1,r,ql,qr);
		push_up(p);
	}
	ll query(int p,int l,int r,int ql,int qr)
	{
		if(ql<=l && r<=qr)
		{
			return t[p].sum;
		}
		int mid=(l+r)>>1;
		ll ans=0;
		if(ql<=mid)
		ans+=query(ls(p),l,mid,ql,qr);
		if(mid<qr)
		ans+=query(rs(p),mid+1,r,ql,qr);
		return ans;
	}
	#undef ls
	#undef rs
}S1;
int main()
{
	iossync;
	int cnt=0;
	while(cin>>n)
	{
		cout<<"Case #"<<++cnt<<":\n";
		rep(i,1,n+1)
		{
			cin>>a[i];
		}
		S1.build(1,1,n);
		cin>>m;
		while(m--)
		{
			cin>>qwe>>l>>r;
			if(l>r)
			swap(l,r);
			switch(qwe)
			{
				case 0:{
					S1.change(1,1,n,l,r);
					break;
				}
				case 1:{
					cout<<S1.query(1,1,n,l,r)<<"\n";
					break;
				}
			}
		}
		cout<<"\n";
	}
	return 0;
}

以及,这题在 l u o g u luogu luogu上有个重题,那题数据范围还比这题小,把 w h i l e ( c i n > > n ) while(cin>>n) while(cin>>n) c a s e case case去掉后,就可以直接 a c ac ac,嘿嘿嘿,白嫖一个蓝题,这是那题的链接
P4145 上帝造题的七分钟2 / 花神游历各国






GSS5 - Can you answer these queries V

从第五题开始, l u o g u luogu luogu上就是紫题了(虽然我个人感觉2跟134压根不是一个难度),
这题就一个操作,查询,但这个查询就很有意思了,查询最大子段和,但左端点必须在 [ x 1 , y 1 ] [x1,y1] [x1,y1]之间,右端点必须在 [ x 2 , y 2 ] [x2,y2] [x2,y2]之间。
一眼看过去,woc,这固定了左右端点咋办嘛,可是仔细一想,啊?左右端点既然是有范围的,那么当 x 2 > y 1 x2>y1 x2>y1的时候,中间的区域不就是必然取的吗,然后左边取rmax,右边取lmax,3者相加,不就是答案了吗,当 x 2 < = y 1 x2<=y1 x2<=y1的时候也就3种情况,把GSS1代码改改就ok了,但我一开始分类的有点多,这是我一开始的分类

while(m--)
		{
			cin>>x1>>y3>>x2>>y2;
			if(y3+1<=x2-1)//不相交且中间有剩余 
			cout<<S1.query(1,1,n,x1,y3).rmax+S1.query(1,1,n,x2,y2).lmax+S1.query(1,1,n,y3+1,x2-1).sum<<"\n";
			else
			if(y3+1==x2)
			{//恰好不相交,中间无剩余 
				cout<<S1.query(1,1,n,x1,y3).rmax+S1.query(1,1,n,x2,y2).lmax<<"\n";
			}
			else
			if(x1==x2 && y3==y2) 
			{//完全重合 
				cout<<S1.query(1,1,n,x1,y3).mx<<"\n";
			}
			else
			if(y3==x2)
			{
				if(x2!=y2)
				{
					ll ans=-inf;
					ans=S1.query(1,1,n,x2,y3).mx;
					ans=max(ans,S1.query(1,1,n,x1,y3).rmax+S1.query(1,1,n,x2+1,y2).lmax);
					if(x1<x2)
					{
						ans=max(ans,S1.query(1,1,n,x1,x2-1).rmax+S1.query(1,1,n,x2,y2).lmax);
					}
					cout<<ans<<"\n";
				}
				else
				{
					cout<<S1.query(1,1,n,x1,y3).rmax<<"\n";
				}
			}
			else
			{//部分重合 
				ll ans=-inf;
				if(x1<x2)
				ans=max(ans,S1.query(1,1,n,x1,x2-1).rmax+S1.query(1,1,n,x2,y2).lmax);
				if(y3+1<=y2)
				ans=max(ans,S1.query(1,1,n,x1,y3).rmax+S1.query(1,1,n,y3+1,y2).lmax);
				ans=max(ans,S1.query(1,1,n,x2,y3).mx); 
				cout<<ans<<"\n";
			}
		}

然后发现压根不用这么细,其实可以浓缩

		while(m--)
		{
			cin>>x1>>y3>>x2>>y2;
			if(y3<x2)
			{
				if(x2>=y3+2)
				cout<<S1.query(1,1,n,x1,y3).rmax+S1.query(1,1,n,x2,y2).lmax+S1.query(1,1,n,y3+1,x2-1).sum<<"\n";
				else
				cout<<S1.query(1,1,n,x1,y3).rmax+S1.query(1,1,n,x2,y2).lmax<<"\n";
			}
			else
			{
				ll ans=-inf;
				ans=S1.query(1,1,n,x2,y3).mx;
				if(x2>=x1+1)
				{
					ans=max(ans,S1.query(1,1,n,x1,x2-1).rmax+S1.query(1,1,n,x2,y2).lmax);
				}
				if(y2>=y3+1)
				{
					ans=max(ans,S1.query(1,1,n,y3+1,y2).lmax+S1.query(1,1,n,x1,y3).rmax);
				}
				cout<<ans<<"\n";
			}
		} 

所以这题就写完了,线段树部分甚至可以照搬之前的,也没change函数了,只需要query函数

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
typedef vector<int> VI;
const ll mod=1000000007;
const int maxn=1e6+10;
//const int inf=0x3f3f3f3f;
const ll inf = 1e18;
//const int inf=0x7fffffff;
ll gcd(ll a,ll b) {
    return b?gcd(b,a%b):a;
}
//	  freopen("1.txt","r",stdin);
//    freopen("2.txt","w",stdout);
#define ms(a) memset(a,0,sizeof(a))
#define mss(a) memset(a,-1,sizeof(a))
#define msi(a) memset(a,inf,sizeof(a))
#define iossync ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head



ll a[maxn];
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
ll n,m;
struct Segment_Tree{
	struct node{
		ll sum,mx;
		ll lmax,rmax;
		node(){
			mx=lmax=rmax=sum=0;
		}
		void init(ll x=0)
		{
			mx=lmax=rmax=sum=x;
		}
		node operator + (const node & b) const
		{
			node ans;
			ans.sum=sum+b.sum;
			ans.lmax=max(lmax,sum+b.lmax);
			ans.rmax=max(rmax+b.sum,b.rmax);
			ans.mx=max(max(mx,b.mx),rmax+b.lmax);
			return ans;
		}
	}t[maxn<<2];
	void push_up(int p)
	{
		t[p]=t[ls(p)]+t[rs(p)];
	}
	void build(int p,int l,int r)
	{
//		t[p].init();
		if(l==r)
		{
			t[p].init(a[l]);
			return ;
		}
		int mid=(l+r)>>1;
		build(ls(p),l,mid);
		build(rs(p),mid+1,r);
		push_up(p);
	}
	node query(int p,int l,int r,int ql,int qr)
	{
		if(ql<=l && r<=qr)
		return t[p];
		int mid=(l+r)>>1;
		if(ql<=mid && mid<qr)
		{
			return query(ls(p),l,mid,ql,qr)+query(rs(p),mid+1,r,ql,qr);
		}
		else
		if(ql<=mid)
		{
			return query(ls(p),l,mid,ql,qr);
		}
		else
		{
			return query(rs(p),mid+1,r,ql,qr);
		}
	}
}S1;
int x1,y3,x2,y2,T;
int main()
{
	iossync;
	cin>>T;
	while(T--)
	{
		cin>>n;
		rep(i,1,n+1)
		{
			cin>>a[i];
		}
		S1.build(1,1,n);
		cin>>m;
		while(m--)
		{
			cin>>x1>>y3>>x2>>y2;
			if(y3<x2)
			{
				if(x2>=y3+2)
				cout<<S1.query(1,1,n,x1,y3).rmax+S1.query(1,1,n,x2,y2).lmax+S1.query(1,1,n,y3+1,x2-1).sum<<"\n";
				else
				cout<<S1.query(1,1,n,x1,y3).rmax+S1.query(1,1,n,x2,y2).lmax<<"\n";
			}
			else
			{
				ll ans=-inf;
				ans=S1.query(1,1,n,x2,y3).mx;
				if(x2>=x1+1)
				{
					ans=max(ans,S1.query(1,1,n,x1,x2-1).rmax+S1.query(1,1,n,x2,y2).lmax);
				}
				if(y2>=y3+1)
				{
					ans=max(ans,S1.query(1,1,n,y3+1,y2).lmax+S1.query(1,1,n,x1,y3).rmax);
				}
				cout<<ans<<"\n";
			}
		} 
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值