线段树一些题
Posted 吃花椒的妙酱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树一些题相关的知识,希望对你有一定的参考价值。
思路:区间mex,树状数组
区间哪些数会变,即求哪些数不会变
先考虑下区间离散化后数组中哪些值会变?区间mex后面的数字都会变,这应该显然,小于mex的数就是不变的了
将问题分解成两个问题,我们设询问区间mex = x,分解为求区间mex和区间有多少数小于x
求区间mex,我们用权值线段树,维护每个数出现的最后位置,区间合并时,维护最左边的最后位置,当所在区间维护的值(最后位置)小于询问的左区间,说明mex在线段树左区间,整体思想是线段树上做二分
为什么要维护最后出现的位置呢,如果一个数在1~mex范围内,那么它最后出现的位置一定大于询问的左区间。
用主席树的话可以在线查询,R(询问的右区间版本)上做二分
用普通线段树的话需要对询问离线,按照询问的右端点排序,再去对线段树做更新操作
题目二
对区间做加平方操作
思路:观察所加的数与下标的关系,第一个数加1,第二个数加2方……
对加 , 展开维护区间下标和区间下标平方和
思路:线段树整体二分
维护区间max,左边max > x就往左走,否则往右走
同时维护区间次数,当次数到达上限时,将max赋值为0即可
思路:线段树维护floyd转移矩阵
一道DDP,带修最短路
我们先来观察矩阵的性质
我们可以用矩阵表示邻接矩阵,a[i][j] = k ,表示i走一步到j有k种走法
我们将矩阵平方a[i][j] = k ,表示i走两步到j有k种走法
我们可以借助网络图来理解下
容易看出从i一步到k有a种走法,k一步到j有b种走法,那i两步到j有a+b种走法,我们将a的定义改成路径长度,这样就类似floyd跑最短路了.
floyd核心式子是a[i][j] = min(a[i][k],a[k][j]),我们将其重定义成矩阵乘法,这样就可以维护待修最短路了
这题是图论+dp的很好结合
#include <cmath>
#include <cstring>
#include <algorithm>
#include <map>
#include <list>
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <deque>
using namespace std;
typedef long long ll;
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define scd(v) scanf("%d",&v)
#define scdd(a,b) scanf("%d %d",&a,&b)
#define endl "\\n"
#define IOS ios::sync_with_stdio(false),cin.tie(0);
#define pb push_back
#define all(v) v.begin(),v.end()
#define int long long
#define odd(x) x&1
#define mst(v,a) memset(v,a,sizeof(v))
#define lson p<<1 ,l,mid
#define rson p<<1|1,mid+1,r
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define pii pair<int,int>
#define inf 0x7f7f7f7f
#define INF 0x3f3f3f3f3f3f3f3f
const int N=1e5+10;
int n,m;
char s[N];
int val0[N],val1[N],val2[N];
map<int ,int > mp;
struct matrix
{
int a[5][5];
matrix()
{
_for(i,1,2) _for(j,1,2) a[i][j] = INF;
}
matrix operator * (const matrix & temp) const
{
matrix res;
_for(i,1,2)
{
_for(j,1,2)
{
_for(k,1,2)
{
res.a[i][j] = min(res.a[i][j],a[i][k]+ temp.a[k][j]);//重定义矩阵乘为floyd
}
}
}
return res;
}
}tr[N*4];
void print(matrix hh)
{
cout<<" ch2 "<<endl;
cout<<hh.a[1][1]<<" "<<hh.a[1][2]<<endl;
cout<<hh.a[2][1]<<" "<<hh.a[2][2]<<endl;
}
void pushup(int p)
{
tr[p] = tr[ls] * tr[rs];//矩阵左乘
}
void build(int p, int l,int r)
{
if(l==r)
{
mp[l] = p;
if( s[l] == '/')
{
tr[p].a[1][1] = val0[l];
tr[p].a[2][2] = val1[l];
tr[p].a[1][2] = val2[l];
}
else
{
tr[p].a[1][1] = val0[l];
tr[p].a[2][2] = val1[l];
tr[p].a[2][1] = val2[l];
}
return;
}
int mid=(l+r)>>1;
build(lson),build(rson);
pushup(p);
}
void change(int x ,matrix y)//单点修改
{
int pos = mp[x];//叶子的位置
tr[pos] = y;
pos>>=1;
while( pos )
{
pushup(pos);
pos>>=1;
}
return;
}
matrix query(int p,int l, int r ,int x ,int y)
{
if( x<=l && r<=y )
{
return tr[p];
}
int mid=(l+r)>>1;
if( y<=mid ) return query(lson,x,y);
else if( x>=mid+1) return query(rson,x,y);
else
{
matrix t1,t2,t3;
t1 = query(lson,x,mid);
t2 = query(rson,mid+1,y);
t3 = t1*t2;
return t3;
}
}
signed main()
{
//!!//
// freopen("data.txt","r",stdin);
// !!
IOS;
cin>>n>>m;
cin>>(s+1);
_for(i,1,n-1)
{
cin>>val0[i]>>val1[i]>>val2[i];
}
build(1,1,n-1);
while( m-- )
{
int op;cin>>op;
if( op==0 )
{
int h;
char ch;
cin>>h>>ch;
if( ch == s[h] ) continue;
s[h] = ch;
matrix temp;
if( s[h] == '/')
{
temp.a[1][1] = val0[h];
temp.a[2][2] = val1[h];
temp.a[1][2] = val2[h];
}
else
{
temp.a[1][1] = val0[h];
temp.a[2][2] = val1[h];
temp.a[2][1] = val2[h];
}
change(h,temp);
}
else if( op==1 )
{
int a,b,c,d;cin>>a>>b>>c>>d;
val0[a] = b;
val1[a] = c;
val2[a] = d;
matrix temp;
if( s[a] == '/')
{
temp.a[1][1] = val0[a];
temp.a[2][2] = val1[a];
temp.a[1][2] = val2[a];
}
else
{
temp.a[1][1] = val0[a];
temp.a[2][2] = val1[a];
temp.a[2][1] = val2[a];
}
change(a,temp);
}
else
{
int a,b,c,d;cin>>a>>b>>c>>d;
int ans=0;
if( !c )
{
if( !d ) ans = query(1,1,n-1,a,b-1).a[1][1];
else ans = query(1,1,n-1,a,b-1).a[1][2];
}
else
{
if( !d ) ans = query(1,1,n-1,a,b-1).a[2][1];
else ans = query(1,1,n-1,a,b-1).a[2][2];
}
if( ans >= INF ) cout<<-1<<endl;//会爆inf要开INF
else cout<<ans<<endl;
}
}
}
以上是关于线段树一些题的主要内容,如果未能解决你的问题,请参考以下文章