线段树一些题

思路:区间mex,树状数组 

区间哪些数会变,即求哪些数不会变

先考虑下区间离散化后数组中哪些值会变?区间mex后面的数字都会变,这应该显然,小于mex的数就是不变的了

将问题分解成两个问题,我们设询问区间mex = x,分解为求区间mex和区间有多少数小于x

求区间mex,我们用权值线段树,维护每个数出现的最后位置,区间合并时,维护最左边的最后位置,当所在区间维护的值(最后位置)小于询问的左区间,说明mex在线段树左区间,整体思想是线段树上做二分

为什么要维护最后出现的位置呢,如果一个数在1~mex范围内,那么它最后出现的位置一定大于询问的左区间。

用主席树的话可以在线查询,R(询问的右区间版本)上做二分

用普通线段树的话需要对询问离线,按照询问的右端点排序,再去对线段树做更新操作

 题目二

对区间做加平方操作

思路:观察所加的数与下标的关系,第一个数加1,第二个数加2方……

a_{l+i}(l+i+1-i)^{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;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值