线段树水题

[THUSCH2017] 大魔法师

​ 给定 \(n\) 个三元组 \((A,B,C)\) 。共有 \(m\) 种区间操作,分为三大类,七小类。

1.\(A_i=A_i+B_i\) 2.\(B_i=B_i+C_i\) 3.\(C_i=C_i+A_i\)

给定值 \(v\) 4. \(A_i=A_i+v\) 5. \(B_i=B_i\times v\) 6. \(C_i=v\)

7.区间查询所有三元组的和对 \(998244353\) 取模的结果。

\(n,m\le 2.5\times10^5\)


直接建立线段树,每个节点上改为维护矩阵。将六种修改操作分别建出转移矩阵即可,需要支持区间乘法。卡常可以使用矩阵乘法循环展开和取模优化。

[NOI2016] 区间

​ 给定 \(n\) 个区间,要从中选出 \(m\) 个区间,使得它们共同包含至少一个位置。对于一个选取方案,其花费为选中的最长区间长度减去最短区间长度。求最小权值,若不存在合法方案输出 \(-1\)

\(n\le 5\times 10^5\;\;m\le 2\times 10^5\;\;0\le l\le r\le 10^9\)


我们可以先对区间长度排个序。然后我们依次将区间加入,利用双指针,如果存在某个位置出现次数大于等于 \(m\) ,我们再不断删去区间,同时不断更新答案。然后我们考虑如何去判断某个位置出现次数大于等于 \(m\) ,我们先将所有区间离散化,然后扔到一棵线段树上,记录区间最大值,维护区间加的操作即可。时间复杂度为 \(O(n\log n)\)

[HEOI2013] Segment

[JSOI2008] Blue Mary 开公司

\(n\) 个操作,包括:加入一条两端点为点 \((x_0,y_0),(x_1,y_1)\) 的线段,询问当 \(x=k\) 时纵坐标最大的那个线段的编号。强制在线。

\(n\le 10^5\)


李超线段树模板题。我们维护区间中的最优线段,我们取当 \(x=mid\) 时值更大的线段为该区间的最优线段,然后我们通过比较 \(l\) 处和 \(r\) 处的值大小,递归地去更新左右区间。注意李超线段树不存在 \(pushup\)\(pushdown\) 等操作。询问的时候直接在树上递归取区间最大值即可,注意一定要递归去取最大值。时间复杂度为 \(O(n\log ^2n)\)

以下代码以 [HEOI2013] Segment 为例。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=1e5+9,mod=39989,mod2=1e9,INF=2e9;
const double eps=1e-10;
typedef long long int ll;
typedef pair <double,int> PDI;
struct seg{
    double k,b;
    int l,r;
} p[N];
struct node{
    int maxx;
} tr[N<<2];
int n,last,tot;
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0' || c>'9') {if(c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9') {x=x*10+c-48,c=getchar();}
    return x*f;
}
inline double get(int id,int x)
{
    if(x<p[id].l || x>p[id].r) return -INF;//不在区间内
    return p[id].k*x+p[id].b;
}
void upd(int k,int l,int r,int L,int R,int id)
{
    if(R<l || r<L) return;
    if(L<=l && r<=R)
    {
        if(tr[k].maxx==0)
        {
            tr[k].maxx=id;
            return;
        }
        int mid=(l+r)>>1;
        if(get(id,mid)-get(tr[k].maxx,mid)>eps) swap(id,tr[k].maxx);//mid处值较大的作为区间代表
        if((get(id,l)-get(tr[k].maxx,l)>eps) || (get(id,l)==get(tr[k].maxx,l) && id<tr[k].maxx)) upd(k<<1,l,mid,L,R,id);
        if((get(id,r)-get(tr[k].maxx,r)>eps) || (get(id,r)==get(tr[k].maxx,r) && id<tr[k].maxx)) upd(k<<1|1,mid+1,r,L,R,id);
        return;
    }
    int mid=(l+r)>>1;
    upd(k<<1,l,mid,L,R,id),upd(k<<1|1,mid+1,r,L,R,id);
}
PDI maxx(PDI a,PDI b)
{
    if(a.first-b.first>eps) return a;
    else if(b.first-a.first>eps) return b;
    else if(a.second<b.second) return a;
    return b;
}
PDI query(int k,int l,int r,int pos)
{
    PDI ans;
    if(tr[k].maxx!=0) ans={get(tr[k].maxx,pos),tr[k].maxx};
    if(l==r) return ans;
    int mid=(l+r)>>1;
    if(pos<=mid) ans=maxx(ans,query(k<<1,l,mid,pos));
    else ans=maxx(ans,query(k<<1|1,mid+1,r,pos));
    return ans;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        int opt=read();
        if(opt==0)
        {
            int k=read();
            k=(k+last-1)%mod+1;
            last=query(1,1,mod,k).second;
            printf("%d\n",last);
        }
        else
        {
            int x0=read(),y0=read(),x1=read(),y1=read();
            x0=(x0+last-1)%mod+1,x1=(x1+last-1)%mod+1,y0=(y0+last-1)%mod2+1,y1=(y1+last-1)%mod2+1;
            ++tot;
            if(x0==x1)
                p[tot].k=0,p[tot].b=max(y0,y1),p[tot].l=p[tot].r=x0;
            else
            {
                p[tot].k=(y1-y0)*1.0/(x1-x0);
                p[tot].b=y0-p[tot].k*x0;
                p[tot].l=min(x0,x1),p[tot].r=max(x0,x1);
            }
            upd(1,1,mod,min(x0,x1),max(x0,x1),tot);
        }
    }
    return 0;
}

[SDOI2016]游戏

​ 给定一棵 \(n\) 个点的带边权树。每个节点上有若干个数字,初始每个节点上为正无穷。有 \(Q\) 次操作,一是在 \(s\)\(t\) 路径上的所有点 \(u\) 上添加一个值为 \(a\times dist_{u,s}+b\) 的数字,二是查询 \(s\)\(t\) 路径上所有点上的数字的最小值。

\(n\le 10^5\;\;Q\le 10^5\)


我们对 \(a\times dist_{u,s}+b\) 这个式子进行一个拆解。令 \(LCA(s,t)=p\)\(dist_i\) 表示从根节点到点 \(i\) 的距离。则在 \(s\)\(p\) 路径上的所有点 \(i\) ,插入了值

\[a\times (dist_s-dist_i)+b=-a\times dist_i+a\times dist_s+b \]

\(p\)\(t\) 路径上的所有点 \(i\) ,插入了值

\[a\times(dist_i+dist_s-2\times dist_p)+b=a\times dist_i+a\times (dist_s-dist_p)+b \]

发现就是加了两条线段,所以我们用李超线段树去维护,同时还要维护一个区间最小值。而对于树上路径我们直接用树剖处理即可。时间复杂度为 \(O(n\log ^3n )\)

posted @ 2023-05-17 14:14  YC乌龙  阅读(31)  评论(0编辑  收藏  举报