线段树练习题

poj-3667–hotel
整体来说思路不难想,就是细节比较多,处理复杂,容易出错

题意:给出长为n的空间大小,支持两种操作
1.给定长度len,尽可能填补靠左边的空间,并输出填补的起始位置
2.给定起始位置pos,以及长度len,将以pos为起始长度为len的空间清空

思路:

变量记录:
维护三颗线段树,seg_left[root]表示结点root对应的区间[l,r]上,从左端点l起始的空闲空间长度,seg_right[root]表示结点root对应的区间[l,r]上,从右端点r起始的空闲空间长度,seg_tree[root]表示结点root对应的区间[l,r]上,对应的最大的连续空闲长度。
lazy[root]表示结点root对应的区间[l,r]上,对应的懒标记,这里需要注意的点是,有三种状态,值为2时表示root结点下的子树都为被占状态,值为1时表示root结点下的子树都为空闲状态,两种后来的可以覆盖前面的,值为0表示没有进行标记。
细节处理方面比较多:
1.push_up向上回溯的处理
2.query查询时的处理,先查左子树,再查左子树加上右子树,最后查右子树
3.lazy tag的部分,push_down向下push比较简单,就是将三颗树的值都置为lazy的值就可以。

//#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

#define int long long
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 2e5+10;
int n;
int arr[N],seg_tree[N<<2],lazy[N<<2],seg_left[N<<2],seg_right[N<<2];
void push_up(int root,int L,int R)
{
    seg_tree[root]=seg_right[root<<1]+seg_left[root<<1|1];
    seg_tree[root]=max(seg_tree[root],max(seg_tree[root<<1],seg_tree[root<<1|1]));
    int mid=L+R>>1;
    if(seg_left[root<<1]==mid-L+1)
    seg_left[root]=seg_left[root<<1]+seg_left[root<<1|1];
    else seg_left[root]=seg_left[root<<1];

    if(seg_right[root<<1|1]==R-mid)
    seg_right[root]=seg_right[root<<1]+seg_right[root<<1|1];
    else seg_right[root]=seg_right[root<<1|1];
}
void build(int root,int L,int R)
{
    if(L==R)
    {
        seg_tree[root]=1;
        seg_left[root]=1;
        seg_right[root]=1;
        return ;
    }
    int mid=(L+R)>>1;
    build(root<<1,L,mid);
    build(root<<1|1,mid+1,R);
    push_up(root,L,R);
}
void add(int root,int L,int R,int val)
{
    seg_tree[root]=(val-1)*(R-L+1);
    seg_left[root]=(val-1)*(R-L+1);
    seg_right[root]=(val-1)*(R-L+1);
}
void push_down(int root,int L,int R)
{
    if(lazy[root])
    {
        int mid=L+R>>1;
        lazy[root<<1]=lazy[root];
        lazy[root<<1|1]=lazy[root];
        add(root<<1,L,mid,lazy[root]);
        add(root<<1|1,mid+1,R,lazy[root]);
        lazy[root]=0;
    }
}

void update_Interval(int root,int L,int R,int LL,int RR,int val)
{
    if(LL>R||RR<L)return ;
    int mid=L+R>>1;
    if(LL<=L&&RR>=R)
    {
        //if(L==R&&L==6)cout<<"fuck"<<endl;
        lazy[root]=val;
        seg_tree[root]=(R-L+1)*(val-1);
        seg_left[root]=(R-L+1)*(val-1);
        seg_right[root]=(R-L+1)*(val-1);
        //lazy标记只是标记当前已加了值,但是还没下传的,所以这里不是*lazy[root]
        //lazy[root]表示的是儿子节点需要加的值
        return ;
    }
    //up和down需要对应,因为up是通过儿子结点对当前节点进行更新
    //所以,pushdown要保证儿子的值已经更新了。
    push_down(root,L,R);

    update_Interval(root<<1,L,mid,LL,RR,val);
    update_Interval(root<<1|1,mid+1,R,LL,RR,val);
    push_up(root,L,R);
}
int ans;
void query(int root,int L,int R,int LL,int RR,int x)
{
    if(LL>R||RR<L)return  ;
    /*if(x==4)
    cout<<L<<" "<<R<<" "<<seg_tree[root]<<endl;*/
    int mid=L+R>>1;
    push_down(root,L,R);
    if(LL<=L&&RR>=R)
    {
        if(L==R)
        {
            ans=L;
            update_Interval(1,1,n,L,L,1);
            return ;
        }
        if(seg_tree[root<<1]>=x)
        {
            query(root<<1,L,mid,LL,RR,x);
        }

        else if(seg_right[root<<1]+seg_left[root<<1|1]>=x)
        {
            int pos;
            pos=mid-seg_right[root<<1]+1;
            //cout<<"update"<<pos<<pos+x-1<<endl;
            update_Interval(1,1,n,pos,pos+x-1,1);
            ans=pos;
            return ;
        }


        else
        {
            query(root<<1|1,mid+1,R,LL,RR,x);
        }

    }
    //push_down(root,L,R);
    push_up(root,L,R);

    //return min(query(root<<1,L,mid,LL,RR,x),query(root<<1|1,mid+1,R,LL,RR,x));
}
void update(int root,int L,int R,int pos,int val)
{
    if(pos<L||pos>R)return ;
    if(L==R&&L==pos)
    {
        seg_tree[root]=val;
        return ;
    }
    int mid=L+R>>1;
    update(root<<1,L,mid,pos,val);
    update(root<<1|1,mid+1,R,pos,val);
    push_up(root,L,R);
}
void func()
{
    cout<<"-------start--------------"<<endl;
    cout<<seg_tree[1]<<endl;
    cout<<seg_tree[2]<<"   "<<seg_tree[3]<<endl;
    cout<<seg_tree[4]<<" "<<seg_tree[5]<<" "<<seg_tree[6]<<" "<<seg_tree[7]<<endl;

    /*cout<<seg_tree[3]<<endl;
    cout<<seg_tree[6]<<"   "<<seg_tree[7]<<endl;
    cout<<seg_tree[12]<<" "<<seg_tree[13]<<" "<<seg_tree[14]<<" "<<seg_tree[15]<<endl;
    cout<<"--------end---------------"<<endl;*/
}

signed main()
{
    ios
    int m;
    cin>>n>>m;
    build(1,1,n);
    while(m--)
    {
        int flag,x,y;
        cin>>flag;
        if(flag==1)
        {
            cin>>x;
            if(seg_tree[1]<x)
            {
                cout<<0<<endl;
            }
            else
            {
                query(1,1,n,1,n,x);
                cout<<ans<<endl;
            }
            //cout<<seg_tree[1]<<endl;
        }
        else
        {
            cin>>x>>y;
            update_Interval(1,1,n,x,x+y-1,2);
        }
        /*cout<<"总共 "<<seg_tree[1]<<endl;
        func();*/
    }
    return 0;
}

/*
9 100
1 3
1 3
1 3
2 1 9
1 3

*/

icpc网络预选赛②L Euler Function

在这里插入图片描述
在这里插入图片描述

思路:
由将要乘的数分解质因数,一个一个乘,对于因子已经包含x的区间,直接裸区间乘,不包含的区间,可以再次深入找,直到单点修改(为什么这样不超时,因为100以内只有25个质数,区间范围为1e5,也就是说最多单点修改251e5log1e5次,是可以的)。

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 1e5+10;
int arr[N],lazy[N<<2],seg_tree[N<<2];
int n,m,p=998244353;
int seg_vis[N][30];
int fi[101];
int prime[30];
int inde;
int prime_index[105];//素数的位置,例如prime_index[2]=0

struct node
{
    int zhi[30];
    int len;
}fenjie[105];


void fenjiezhi()
{
    for(int i=2;i<=100;i++)
    {
        int j=0;
        int x=i;
        while(j<inde&&x!=1)
        {
            if(x%prime[j]==0)
            {
                fenjie[i].zhi[fenjie[i].len++]=prime[j];
                while(x%prime[j]==0)
                {
                    x/=prime[j];
                }
            }
            j++;
        }
    }
}
void push_up(int root,int L,int R)
{
    seg_tree[root]=(seg_tree[root<<1]+seg_tree[root<<1|1])%p;
    for(int i=0;i<25;i++)
    {
        seg_vis[root][i]=min(seg_vis[root<<1][i],seg_vis[root<<1|1][i]);
    }
}
void build(int root,int L,int R)
{
    lazy[root]=1;

    if(L==R)
    {
        int x=arr[L];
        seg_tree[root]=x;

        for(int i=0;i<fenjie[x].len;i++)
        {
            int tmp_prime=fenjie[x].zhi[i];
            seg_vis[root][prime_index[tmp_prime]]=1;
            seg_tree[root]=seg_tree[root]*(tmp_prime-1)/tmp_prime;
        }
        return ;
    }
    int mid=(L+R)>>1;
    build(root<<1,L,mid);
    build(root<<1|1,mid+1,R);
    push_up(root,L,R);
}
void add(int root,int L,int R,int val)
{
    lazy[root]=lazy[root]*val%p;
    seg_tree[root]=seg_tree[root]*val%p;
}
void push_down(int root,int L,int R)
{
    //if(lazy[root]!=0)
    //if(root==2)cout<<"fuck"<<lazy[root][0]<<endl;
    if(lazy[root]!=1)
    {
        int mid=L+R>>1;
        add(root<<1,L,mid,lazy[root]);
        add(root<<1|1,mid+1,R,lazy[root]);
    }

    lazy[root]=1;

    //if(root==2)cout<<"fuck"<<lazy[root][0]<<endl;
}

void update_Interval(int root,int L,int R,int LL,int RR,int val)
{
    if(LL>R||RR<L)return ;
    //cout<<L<<" "<<R<<endl;
    int mid=L+R>>1;
    if(LL<=L&&RR>=R)
    {

        int index=prime_index[val];//得到质数的下标
        if(L==R)
        {
            if(seg_vis[root][index]==1)seg_tree[root]=seg_tree[root]*(val)%p;
            else seg_tree[root]=seg_tree[root]*(val-1)%p;
            seg_vis[root][index]=1;
            return ;
        }
        if(seg_vis[root][index])
        {
            seg_tree[root]=seg_tree[root]*val%p;
            lazy[root]=lazy[root]*val%p;
            return ;
        }
        //lazy标记只是标记当前已加了值,但是还没下传的,所以这里不是*lazy[root]
        //lazy[root]表示的是儿子节点需要加的值
    }
    //up和down需要对应,因为up是通过儿子结点对当前节点进行更新
    //所以,pushdown要保证儿子的值已经更新了。
    push_down(root,L,R);

    update_Interval(root<<1,L,mid,LL,RR,val);
    update_Interval(root<<1|1,mid+1,R,LL,RR,val);
    push_up(root,L,R);
}
int query(int root,int L,int R,int LL,int RR)
{
    if(LL>R||RR<L)return  0;
    int mid=L+R>>1;
    if(LL<=L&&RR>=R)
    {
        return seg_tree[root]%p;
    }
    push_down(root,L,R);
    return (query(root<<1,L,mid,LL,RR)+query(root<<1|1,mid+1,R,LL,RR))%p;
}
/*
void update(int root,int L,int R,int pos,int val)
{
    if(pos<L||pos>R)return ;
    if(L==R&&L==pos)
    {
        seg_tree[root]=val;
        arr[L]=val;
        return ;
    }
    int mid=L+R>>1;
    update(root<<1,L,mid,pos,val);
    update(root<<1|1,mid+1,R,pos,val);
    push_up(root,L,R);
}
void func()
{
    cout<<"-------start--------------"<<endl;
    cout<<seg_tree[1]<<endl;
    cout<<seg_tree[2]<<"   "<<seg_tree[3]<<endl;
    cout<<seg_tree[4]<<" "<<seg_tree[5]<<" "<<seg_tree[6]<<" "<<seg_tree[7]<<endl;
    cout<<seg_tree[8]<<" "<<seg_tree[9]<<endl;
    cout<<"--------end---------------"<<endl;
    cout<<seg_tree[3]<<endl;
    cout<<seg_tree[6]<<"   "<<seg_tree[7]<<endl;
    cout<<seg_tree[12]<<" "<<seg_tree[13]<<" "<<seg_tree[14]<<" "<<seg_tree[15]<<endl;

}*/
signed main()
{
    ios
    for(int i=1;i<=100;i++)
    {
        int sum=1;
        //int flag=1;
        for(int j=2;j<i;j++)
        {
            if(__gcd(i,j)==1)sum++;

        }
        fi[i]=sum;
        if(fi[i]==i-1)
        {
            prime[inde]=i;
            prime_index[i]=inde++;
        }
        //cout<<i<<" "<<sum<<endl;
    }
    //cout<<inde<<endl;
    fenjiezhi();
    //for(int i=0;i<=100;i++)
        //cout<<"i="<<i<<" "<<prime_index[i]<<endl;
    //cout<<fenjie[8].len;
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>arr[i];
    build(1,1,n);
    //func();
    while(m--)
    {
        int flag,x,y,k;
        cin>>flag;
        if(flag==0)
        {
            cin>>x>>y>>k;
            int zhi=k;
            for(int i=0;i<fenjie[k].len;i++)
            {
                int tmp_prime=fenjie[k].zhi[i];//分解的每一个质数

                //lazy[root][index]++;//对应的懒标记++记录
                while(zhi%tmp_prime==0)
                {
                    update_Interval(1,1,n,x,y,tmp_prime);
                    zhi/=tmp_prime;
                }
            }
        }
        else
        {
            cin>>x>>y;
            cout<<query(1,1,n,x,y)<<endl;
        }
        //func();
    }
    return 0;
}

/*


*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值