2023.8.6

2022河南萌新联赛第(三)场:河南大学\区间操作.cpp

//题意:定义一个f[x]函数表示一个数分解质因数后各个质因子的幂次和,给定一个长度为n的数组,

//有m个操作,第一种操作是输出[l, r]范围内的a[i]的乘积的f[i],另一种操作是整段区间乘上一个数。

//思路:区间内所有数的乘积的f[i]= 每个数的f[i]的和  (因为数相乘 = 幂次方相加)

//于是们开一个线段树表示区间[l, r]的f[i]和,可以直接查询[l, r]范围内的a[i]的乘积的f[i]。

//对于修改,一个区间乘一个v,如果对v作因数分解,其实就是区间加上因数分解后所有幂次的和,也就是区间加,我们用线段树很容易实现。

//欧拉筛,预处理出范围内所有质数

#include<bits/stdc++.h>

#include<iostream>

#include<algorithm>

#include<map>

#include<set>

#include<queue>

#include<cstring>

#include<math.h>

#include<map>

#include<vector>

#include<stack>

using namespace std;



#define endl '\n'

typedef pair<int,int> pr;



#define int long long

#define ll long long

#define fr(i,l,r) for(int i=l;i<=r;i++)

#define ufr(i,n,z) for(int i = n;i >= z; i--)

#define pb(x) push_back(x)

#define all(a) a.begin(),a.end()

#define fi first

#define se second



const int N = 1e6+10;

const int mod=998244353,inf=LONG_LONG_MAX;

int n,m;



int a[N];

int cnt;

int st[N];

int prime[N];

struct node{

    int l,r;

    int sum;     //和

    int flag;

}tree[N<<2];

void init(){                //欧拉筛

    for(int i=2;i<N;i++){

        if(!st[i])prime[cnt++]=i;

        for(int j=0;i*prime[j]<N;j++){

            st[i*prime[j]]=1;

            if(i%prime[j]==0){

                break;

            }

        }

    }

}

int get(int x){

    int ans=0;

    if(x==1) return 0;

    for(int i=0;prime[i]*prime[i]<=x;i++){

        int p=prime[i];

        if(x%p==0){

            while(x%p==0){

                ans++;

                x/=p;

            }

        }

    }

    if(x>1)ans++;

    return ans;

}

void push_up(int p){

    tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;

}

void build(int p,int l,int r){

    tree[p].l=l;

    tree[p].r=r;

    if(l==r){

        tree[p].sum=a[l];

        return;

    }

    int mid=l+r>>1;

    int f=p<<1;

    build(f,l,mid);

    build(f+1,mid+1,r);

    push_up(p);

}

void push_down(int p){

    if(tree[p].flag){

        int f=p<<1;

        tree[f].sum+=(tree[f].r-tree[f].l+1)*tree[p].flag;

        tree[f+1].sum+=(tree[f+1].r-tree[f+1].l+1)*tree[p].flag;

        tree[f].flag+=tree[p].flag;

        tree[f+1].flag+=tree[p].flag;

        tree[p].flag=0;

    }

}

void update(int p,int l,int r,int w){

      if(l<=tree[p].l&&tree[p].r<=r){

          tree[p].sum+=(tree[p].r-tree[p].l+1)*w;

          tree[p].flag+=w;

           return ;

      }

      push_down(p);

      int mid=tree[p].l+tree[p].r>>1;

      int f=p<<1;

      if(l<=mid)

      update(f,l,r,w);

      if(mid<r)

      update(f+1,l,r,w);

      push_up(p);

}

int query(int p,int l,int r){

    if(l<=tree[p].l&&tree[p].r<=r){

        return tree[p].sum;

    }

    push_down(p);

    int ans=0;

    int mid=tree[p].l+tree[p].r>>1;

    int f=p<<1;

    if(l<=mid)

    ans+=query(f,l,r);

    if(mid<r)

    ans+=query(f+1,l,r);

    return ans;

}

void solve()

{

    init();

    int n;

    cin>>n;

    fr(i,1,n){

        cin>>a[i];

        a[i]=get(a[i]);

    }

    build(1,1,n);

    int q;

    cin>>q;

    while(q--){

        int op,l,r,w;

        cin>>op>>l>>r;

        if(op==1){

          cout<< query(1,l,r)<<'\n';

        }

        else{

            cin>>w;

            w=get(w);

            update(1,l,r,w);

              //cout<<1<<'\n';

        }

    }

}



signed main()

{

    int t=1;

 //   cin>>t;

    while(t--) solve();

    return 0;

}

2022河南萌新联赛第(一)场:河南工业大学\热身小游戏.cpp

//给定一个数n,初始值为1

//给出q次操作,

//1 a,n->n*a

//2 l r撤销第l到r操作中的1操作

//3输出n(mod 1e9+7)

//思路:起初以为是可持久化线段树,特地学了一下,后来发现就是线段树

q次操作看做长度为q的序列,操作1单点修改,操作2区间修改为1,操作3整个区间乘积

#include <bits/stdc++.h>

#define fr(i,l,r) for(int i=l;i<=r;i++)

using namespace std;



#define int long long

#define endl "\n"

const int max_n = 1e5 + 10;

const int inf = 0x3f3f3f3f;

const int mod = 1e9 + 7;



int n, q;

struct Node

{

    int l, r, add, v;

} tr[max_n << 2];



void pu(int u)

{

    tr[u].v = tr[u << 1].v * tr[u << 1 | 1].v % mod;

}

void pd(int u)

{

    if (tr[u].add)

    {

        tr[u << 1].v = 1, tr[u << 1 | 1].v = 1;

        tr[u << 1].add = 1, tr[u << 1 | 1].add = 1;

        tr[u].add = 0;

    }

}

void build(int u, int l, int r)

{

    tr[u] = {l, r, 0, 1};

    if (l == r)

        return;



    int mid = l + r >> 1;

    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);

    pu(u);

}



void modify(int u, int l, int r, int c)

{

    if (tr[u].l >= l && tr[u].r <= r)

    {

        tr[u].add = c;

        tr[u].v = c;

        return;

    }

    pd(u);

    int mid = tr[u].l + tr[u].r >> 1;

    if (l <= mid)

        modify(u << 1, l, r, c);

    if (r > mid)

        modify(u << 1 | 1, l, r, c);

    pu(u);

}



signed main()

{

    ios::sync_with_stdio(false);

    cin.tie(0);

    cout.tie(0);

    cin >> q;



    build(1, 1, q);

    for (int i = 1; i <= q; i++)

    {

        int op;

        cin >> op;

        if (op == 1)

        {

            int a;

            cin >> a;

            modify(1, i, i, a);        //此处单点修改不影响push_down,影响push_up

        }

        else if (op == 2)

        {

            int l, r;

            cin >> l >> r;

            modify(1, l, r, 1);

        }

        else if (op == 3)

            cout << tr[1].v % mod << endl;

    }

    return 0;

}

P3919 【模板】可持久化线段树 1(可持久化数组)
维护长度为N的数组的值,支持以下操作
修改某个历史版本的值,访问某个历史版本的值
v 1 loc value在版本v上将aloc的值改为value
v 2 loc在版本v上访问aloc的值


动态开点的方法存储每个点的左右子节点
显然对于每一次修改,我们都需要创造出一个新的树根 root
然后显然对于新的线段树中的任意节点,如果以该节点代表的区间中不包含被修改的节点,那么也就意味着以这个节点为根的子树不需要修改。
于是我们就可以让这个节点的父亲节点的与这个节点对应的儿子直接指向修改前的线段树上的该节点。
若该节点即对应被修改的节点,那么就可以直接建立新节点。
若该节点对应的区间中包含该节点,那么我们再对该节点的左右儿子分别处理即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
struct node {
    int l, r, t;
}tree[N << 4];
int n, m, tot, a[N], root[N];//Root[i]为版本i的线段树根节点
void build(int& p, int l, int r)
{
    p = ++tot;//给节点赋编号
    if (l == r) {//若当前节点只对应一点
        tree[p].t = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(tree[p].l, l, mid);
    build(tree[p].r, mid + 1, r);
}
void insert(int& p, int l, int r, int pos, int val, int now)//插入,now为版本,pos为修改点位置
{
    tree[p = ++tot] = tree[now];//使当前路径上的点等于原来版本上的对应点
    if (l == r) {
        tree[p].t = val;
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid)
        insert(tree[p].l, l, mid, pos, val, tree[now].l);
    else
        insert(tree[p].r, mid + 1, r, pos, val, tree[now].r);
}
int query(int p, int l, int r, int pos)
{
    if (l == r)
        return tree[p].t;
    int mid = (l + r) >> 1;
    if (pos <= mid)
        return query(tree[p].l, l, mid, pos);
    else
        return query(tree[p].r, mid + 1, r, pos);
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d", a + i);
    build(root[0], 1, n);
    for (int i = 1; i <= m; ++i) {
        int opt, v, loc, val;//opt为操作种类,v为版本,loc为修改/查询点位置,val为修改值
        scanf("%d%d%d", &v, &opt, &loc);
        if (opt == 1) {
            scanf("%d", &val);
            insert(root[i], 1, n, loc, val, root[v]);
        }
        else
            printf("%d\n", query(root[i] = root[v], 1, n, loc));//查询,同时因为查询生成不变的新版本所以Root[i]=Root[v]
    }
    return 0;
}

牛客\多校\2022河南萌新联赛第(二)场:河南理工大学\数对.cpp

//给定2e5的数列,x,y

//1<=l<=r<=n

//求多少对(l,r)满足al+...+ar<=x+y*(r-l+1)   (-1e9<=ai<=1e9)

//思路:转化成(al-y)+....+(ar-y)<=x,令bi=ai-y,求前缀和

//进一步转化成sumr-suml<=x,sumr-x<=suml,枚举右端点,每次累加树状数组大于等于sumr-x的个数

//值域很大,树状数组装不下,考虑离散化

 

#include<bits/stdc++.h>

#include<iostream>

#include<algorithm>

#include<map>

#include<set>

#include<queue>

#include<cstring>

#include<math.h>

#include<map>

#include<vector>

#include<stack>

using namespace std;



#define endl '\n'

typedef pair<int,int> pr;



#define int long long

#define ll long long

#define fr(i,l,r) for(int i=l;i<=r;i++)

#define ufr(i,n,z) for(int i = n;i >= z; i--)

#define pb(x) push_back(x)

#define all(a) a.begin(),a.end()

#define fi first

#define se second



const int N = 1e6+10;

const int mod=998244353,inf=LONG_LONG_MAX;

int n,m;



int a[N];

int tree[N];

int sum[N];

vector<int>v;

int lowbit(int x){return x&(-x);}

void add(int x){

   for(int i=x;i<N;i+=i&-i){

       tree[i]+=1;

   }

}

int query(int x){

   int ans=0;

    for(int i=x;i;i-=i&-i){

        ans+=tree[i];

    }

    return ans;

}

int get(int x){

   return lower_bound(v.begin(),v.end(),x)-v.begin()+1;

}

void solve(){

    int x,y;

    cin>>n>>x>>y;

    fr(i,1,n){

        cin>>sum[i];

        sum[i]+=sum[i-1]-y;

    }

    fr(i,0,n){

        v.push_back(sum[i]);

        v.push_back(sum[i]-x);

    }

    sort(v.begin(),v.end());

    v.erase(unique(v.begin(),v.end()),v.end());

    int ans=0;

    fr(i,0,n){

        ans+=i-query(get(sum[i]-x)-1);

        add(get(sum[i]));

    }

    cout<<ans<<'\n';

   

}



signed main()

{

    int t=1;

 //   cin>>t;

    while(t--) solve();

    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值