线段树专题
Acwing 245. 你能回答这些问题吗
思路
题目问的是区间[x,y]最大连续子段和tmax
如果用线段树求解,那么就需要维护以下信息
我们可以利用分治的思想,将一个子段分为左右两个最大连续子段和,lmax,rmax;
同时需要维护左右区间的sum;那么根节点 u.lmax = max(u.lmax,左sum+右lmax),同理维护u.rmax
那么任意区间的最大连续和就可以分为三种情况
一种是只在左儿子的tmax,一种是在右儿子的tmax,还有一个就是横跨左右儿子 即 左二子的rmax+右儿子的lmax;
void pushup(Node &u,Node &l,Node &r)
{
u.sum=l.sum+r.sum;
u.lmax=max(l.lmax,l.sum+r.lmax);
u.rmax=max(r.rmax,r.sum+l.rmax);
u.tmax=max(max(l.tmax,r.tmax),r.lmax+l.rmax);
}
求得最大值即可;
那么线段树只需要用到单点修改和区间查询即可
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int N = 5e+5;
int a[N];
struct Node {
int l,r;
int lmax,rmax,tmax,sum;
} tre[N<<2];
void pushup(Node &u,Node &l,Node &r) {
u.sum=l.sum+r.sum;
u.lmax=max(l.lmax,l.sum+r.lmax);
u.rmax=max(r.rmax,r.sum+l.rmax);
u.tmax=max(max(l.tmax,r.tmax),r.lmax+l.rmax);
}
void pushup(int rt) {
pushup(tre[rt],tre[rt*2],tre[rt*2+1]);
}
void build(int rt,int l,int r) {
if(l == r) {
tre[rt]= {l,r,a[l],a[l],a[l],a[l]};
} else {
tre[rt]= {l,r};
int mid=(l+r)/2;
build(rt*2,l,mid);
build(rt*2+1,mid+1,r);
pushup(rt);
}
}
Node query(int rt,int l,int r) {
if(tre[rt].l>=l&&tre[rt].r<=r) {
return tre[rt];
}
else {
int mid = (tre[rt].l+tre[rt].r)/2;
if(r<=mid)
return query(rt*2,l,r);
else if(l>mid)
return query(rt*2+1,l,r);
else {
auto Left=query(rt*2,l,r);
auto Right=query(rt*2+1,l,r);
Node res;
pushup(res,Left,Right);
return res;
}
}
}
void modify(int rt,int x,int v) {
if(tre[rt].l==x&&tre[rt].r==x) {
tre[rt]= {x,x,v,v,v,v};
} else {
int mid = (tre[rt].l+tre[rt].r)/2;
if(x<=mid)
modify(rt*2,x,v);
else
modify(rt*2+1,x,v);
pushup(rt);
}
}
signed main() {
int n,m;
cin >> n >> m;
for(int i=1; i<=n; i++) {
cin >> a[i];
}
build(1,1,n);
while (m -- ) {
int k,x,y;
cin >> k >> x >> y;
if(k == 1) {
if(x>y)
swap(x,y);
Node ans = query(1,x,y);
cout<<ans.tmax<<endl;
} else {
modify(1,x,y);
}
}
return 0;
}
246. 区间最大公约数
如果想要维护一个区间的gcd,并进行区间修改的操作很难,但如果根据gcd的这个性质
gcd(a,b)=gcd(a,b−a)
便可以用线段树维护差分去做,区间修改就很容易了,只需要单点修改左右端点即可
重点是如何用线段树利用差分信息去求区间[l,r]的gcd
根据gcd(a,b,c)=gcd(a,b-a,c-b);
我们第一个需要求a是多少,那就是1-l的sum;于是线段树需要维护一个区间sum信息
第二个就是去维护一个区间的gcd信息,去将gcd(b-a,c-b)求出来
void pushup(Node &u,Node &l,Node &r)
{
u.sum=l.sum+r.sum;
u.d=gcd(l.d,r.d);
}
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5e+5;
typedef long long ll;
ll a[N];
struct Node
{
int l,r;
ll sum,d;
}tre[N<<2];
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
void pushup(Node &u,Node &l,Node &r)
{
u.sum=l.sum+r.sum;
u.d=gcd(l.d,r.d);
}
void pushup(int rt)
{
pushup(tre[rt],tre[rt*2],tre[rt*2+1]);
}
void build(int rt,int l,int r)
{
if(l==r)
{
tre[rt]={l,r,a[l]-a[l-1],a[l]-a[l-1]};
}
else
{
tre[rt]={l,r};
int mid=(l+r)/2;
build(rt*2,l,mid);
build(rt*2+1,mid+1,r);
pushup(rt);
}
}
void modify(int rt,int x,ll d)
{
if(tre[rt].l==x&&tre[rt].r==x)
{
ll b=tre[rt].sum+d;
tre[rt]={x,x,b,b};
}
else
{
int mid = (tre[rt].l+tre[rt].r)/2;
if( x <= mid)
modify(rt*2,x,d);
else
modify(rt*2+1,x,d);
pushup(rt);
}
}
Node query(int rt,int l,int r)
{
if(tre[rt].l>=l&&tre[rt].r<=r)
{
return tre[rt];
}
else
{
int mid=(tre[rt].l+tre[rt].r)/2;
if( r <= mid)
{
return query(rt*2,l,r);
}
else if(l > mid)
{
return query(rt*2+1,l,r);
}
else
{
auto left=query(rt*2,l,r);
auto right=query(rt*2+1,l,r);
Node res;
pushup(res,left,right);
return res;
}
}
}
int main()
{
int n,m;
cin >> n >> m;
for(int i=1;i<=n;i++)
cin>>a[i];
build(1,1,n);
while(m --)
{
char op[2];
cin>>op;
if(*op=='Q')
{ int l,r;
cin >> l >> r;
Node res={0,0,0,0};
Node ans = query(1,1,l);
if(l+1<=r)
res = query(1,l+1,r);
ll se=gcd(ans.sum,res.d);
cout<<abs(se)<<endl;
}
else
{
int l,r;
ll d;
cin >> l >> r >> d;
modify(1,l,d);
if(r+1<=n)
modify(1,r+1,-d);
}
}
}
贴广告 HDU - 2795
思路
单点修改+区间查询,将1-h区间以初始权值w建树(需要注意的是如果h>n,则很多浪费,只需要 建1-n即可,如果不考虑直接以1-h建树会re的),维护一个区间能贴广告的最大值,如果tre[1].v < x,则输出-1,否则 优先选左儿子,不行就选右儿子
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#define endl '\n'
#define ll long long
#define PII pair<int,int>
using namespace std;
void IOS() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
const int N=2e5+5;
struct Node {
int l,r;
int v;
} tre[N<<2];
void pushup(int rt) {
tre[rt].v=max(tre[rt*2].v,tre[rt*2+1].v);
}
void build(int rt,int l,int r,int w) {
if(l == r) {
tre[rt] = {l,r,w};
} else {
tre[rt] = {l,r};
int mid = (tre[rt].l+tre[rt].r)/2;
build(rt*2,l,mid,w);
build(rt*2+1,mid+1,r,w);
pushup(rt);
}
}
void query(int rt,int d,int l,int r) {
if(tre[rt].l==tre[rt].r) {
tre[rt].v -= d;
printf("%d\n",tre[rt].l);
return;
}
int mid = (l+r)/2;
if(tre[rt*2].v>=d) {
query(rt*2,d,l,mid);
} else
query(rt*2+1,d,mid+1,r);
pushup(rt);
}
int main() {
int h,w,n;
while(~scanf("%d%d%d",&h,&w,&n)) {
if(h > n) h = n;
memset(tre,0,sizeof(tre));
build(1,1,h,w);
for(int i=1; i<=n; i++) {
int d;
scanf("%d",&d);
if(tre[1].v<d) {
printf("-1\n");
continue;
} else {
query(1,d,1,h);
}
}
}
}