题目:https://icpcarchive.ecs.baylor.edu/external/39/3938.pdf
详见注释
线段树
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<climits>
#include<queue>
#include<vector>
#include<map>
#include<sstream>
#include<set>
#include<stack>
#include<cctype>
#include<utility>
#pragma comment(linker, "/STACK:102400000,102400000")
#define PI 3.1415926535897932384626
#define eps 1e-10
#define sqr(x) ((x)*(x))
#define FOR0(i,n) for(int i=0 ;i<(n) ;i++)
#define FOR1(i,n) for(int i=1 ;i<=(n) ;i++)
#define FORD(i,n) for(int i=(n) ;i>=0 ;i--)
#define lson o<<1,le,mid
#define rson o<<1|1,mid+1,ri
#define MID int mid=(le+ri)>>1
#define zero(x)((x>0? x:-x)<1e-15)
#define mk make_pair
#define _f first
#define _s second
using namespace std;
//const int INF= ;
typedef long long ll;
//const ll inf =1000000000000000;//1e15;
//ifstream fin("input.txt");
//ofstream fout("output.txt");
//fin.close();
//fout.close();
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
const int INF =0x3f3f3f3f;
const int maxn=500000+20 ;
//const int maxm= ;
//by yskysker123
int n,m;
typedef pair<int,int > INTERVAL;
int pre[4*maxn],suf[4*maxn];
ll S[maxn];
INTERVAL sub[4*maxn];
int LE,RI;
//首先a ,a+1,a+2,...,b之和=S[b]-S[a-1],本来是很自然的,放在线段树里就不会用了。
//然后写的代码自然超长无比,要记录过多的信息,
//线段树的pre[]表示某一段前缀和最大时结尾位置
//线段树的suf[]表示某一段后缀和最大时起始位置
//这与传统的记录和或最大的用法不同,对于这题这样真方便,记录了位置+事先预处理,就能在O(1)时间内求出某段和
ll sum(int a,int b)
{
return S[b]-S[a-1];//草掉了-1
}
ll sum( INTERVAL x )
{
return S[x.second]-S[x.first-1];//草掉了-1
}
INTERVAL better(INTERVAL a,INTERVAL b) //充分利用pair的排序,大大简化了代码。
{ //这一题,让我第一次体会到pair到底好用在哪
if(sum(a)!=sum(b)) return sum(a) > sum(b) ? a : b;
return a<b?a:b;
}
void build(int o,int le,int ri)
{
if(le==ri)
{
pre[o]=suf[o]=le; //记录的是位置,而不是值。
sub[o]=mk(le,ri);
return ;
}
MID;
build(lson);
build(rson);
sub[o]= better( sub[2*o] ,sub[2*o+1] ); //此处写错一次 ,应该是两个sub而不是fur和pre!!!!!!!!
sub[o]= better( mk(suf[2*o],pre[2*o+1]) , sub[o] );
ll t1,t2;
t1=sum( le , pre[2*o] );
t2=sum( le , pre[2*o+1] );
pre[o]=t1>=t2? pre[2*o] :pre[2*o+1];
t1=sum( suf[2*o],ri );
t2=sum( suf[2*o+1],ri );
suf[o]=t1>=t2?suf[2*o] :suf[2*o+1] ;
}
INTERVAL Query_pre(int o,int le,int ri)//ri(此时考虑的范围)可能会大于RI(规定范围)
{
if(pre[o]<=RI) //所以,如果该段的pre[]比RI还小,那么直接返回答案
{ //主要考虑的是RI, LE <= le ;
return mk(le,pre[o] );
}
MID;
if(RI<=mid) return Query_pre(lson); //如果RI小于mid 进一步递归求解
return better( mk(le,pre[2*o] ) , mk(le,Query_pre(rson).second ) );//如不是,
//起点都是le,一个终点在lson,一个终点在rson。
}
INTERVAL Query_suf(int o,int le,int ri)
{
if( suf[o]>=LE )//LE>=le是错的,这里要特别注意
{
return mk(suf[o],ri);
}
MID;
if(LE>mid) return Query_suf(rson);
return better( mk( Query_suf(lson).first,ri ),mk( suf[2*o+1],ri ) ); //这里易写错
}
INTERVAL Query(int o,int le,int ri)//递归不断进行,如果某次满足if( LE<=le&&ri<=RI) 则执行语句函数结束。
{ //如果不满足那么若有if(RI<=mid) 或者 else if(LE>mid) 递归。
if( LE<=le&&ri<=RI)
{
return sub[o];
}
MID;
if(RI<=mid) return Query(lson );
else if(LE>mid) return Query(rson );
//如果这些都不满足,那么:
INTERVAL a=Query_suf(lson);
INTERVAL b=Query_pre(rson);
INTERVAL ans=better(Query(lson),Query(rson)); //建树是这里有类似的问题发生,所以我猜这里有,果然有!!!
return better( mk(a.first , b.second), ans );//记住是一个前缀一个后缀 与 前面的部分和 与 后面的部分和 相比较
}
int main()
{
int kase=0;
ll x;int a,b;
while(scanf("%d%d",&n,&m)==2)
{
S[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&x);
S[i]=S[i-1]+x;
}
build(1,1,n);
printf("Case %d:\n",++kase);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
LE=a,RI=b;
INTERVAL t= Query(1,1,n);
printf("%d %d\n",t.first,t.second);
}
}
return 0;
}