2021ICPC网络赛第二场The 2021 ICPC Asia Regionals Online Contest (II) 【L Euler Function】

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

分析:
根据欧拉函数的那个性质

	if(p是质数)
	{
		if(i % p == 0) f[i * p] = f[i] * p;
		else f[i * p] = f[i] * (p - 1);
	}

每次区间乘的那个数小于等于100,所以我们可以考虑把100以内的数质因数分解,区间乘100相当于区间乘两个2和两个5,但是根据那个性质,又分为了两种情况,到底需要乘p还是p - 1?对于每个区间我们可以维护一个bitset<30> tg,表示这个区间的数是否所有的都是第i个素数的倍数,显然,经过几次操作之后,大部分的区间的每个tg值都会变为1,此时都是乘p,比较像区间加/减lowbit的那个题(传送门)。这两道题用的这种线段树,据说是叫势能线段树来着好像。

另外,用这个bitset,实际可以用bool数组的,但实测用bitset约250ms,bool数组490ms,另外bitset可以一下子赋值,不需要跑for循环一个一个的赋值

AC代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const ll mod = 998244353;
bitset<30> st[105];//st[i][j]表示数字i能否被第j个质数整除
struct node
{
    bitset<30> tg;
    ll x, lazy;
    int l, r;
} tree[400050];
int n, m;
int ar[100050];
int pri[30];//100以内的质数,一共25个
int cnt[105][30];//cnt[i][j]表示数字i质因数分解后需要cnt[i][j]个第j个质数
int f[105];//欧拉函数

void dio()//预处理数组pri和cnt
{
    int tot = 0;
    for(int i = 2; i <= 100; ++i)
    {
        bool flag = 0;
        for(int j = 2; j <= sqrt(i); ++j)
        {
            if(i % j == 0)
            {
                flag = 1;
                break;
            }
        }
        if(!flag) pri[++tot] = i;
    }
    for(int i = 1; i <= 100; ++i)
    {
        for(int j = 1; j <= 25; ++j)
        {
            int tmp = i;
            while(tmp % pri[j] == 0)
            {
                ++cnt[i][j];
                tmp /= pri[j];
            }
            st[i][j] = cnt[i][j];
        }
    }
}

int phi(int n)//计算欧拉函数
{
    int ans = n;
    for(int i = 2; i * i <= n; ++i)
    {
        if(n % i == 0)
        {
            ans = ans / i * (i - 1);
            while(n % i == 0)   n /= i;
        }
    }
    if(n > 1)   ans = ans / n * (n - 1);
    return ans;
}

void pushup(int p)
{
    tree[p].x = (tree[p<<1].x + tree[p<<1|1].x) % mod;
    tree[p].tg = (tree[p<<1].tg & tree[p<<1|1].tg);
}

void pushdown(int p)
{
    tree[p<<1].lazy = (tree[p<<1].lazy * tree[p].lazy) % mod;
    tree[p<<1|1].lazy = (tree[p<<1|1].lazy * tree[p].lazy) % mod;
    tree[p<<1].x = (tree[p<<1].x * tree[p].lazy) % mod;
    tree[p<<1|1].x = (tree[p<<1|1].x * tree[p].lazy) % mod;
    tree[p].lazy = 1;
}

void build(int p, int l, int r)
{
    tree[p].l = l;
    tree[p].r = r;
    tree[p].lazy = 1;
    if(tree[p].l == tree[p].r)
    {
        tree[p].x = 1ll * f[ar[l]];
        tree[p].lazy = 1;
        tree[p].tg = st[ar[l]];
        return ;
    }

    int mid = (l + r) >> 1;
    build(p<<1, l, mid);
    build(p<<1|1, mid + 1, r);
    pushup(p);
}

ll query(int p, int a, int b)
{
    if(a <= tree[p].l && tree[p].r <= b) return tree[p].x;

    if(tree[p].lazy != 1) pushdown(p);
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(mid >= b) return query(p<<1, a, b);
    else if(mid < a) return query(p<<1|1, a, b);
    else return (query(p<<1, a, b) + query(p<<1|1, a, b)) % mod;
}

void updata(int p, int a, int b, int x, int y)
{
    if(a <= tree[p].l && tree[p].r <= b && tree[p].tg[x])
    {
        for(int i = 1; i <= y; ++i)
        {
            tree[p].x = (tree[p].x * pri[x]) % mod;
            tree[p].lazy = (tree[p].lazy * pri[x]) % mod;
        }
        return ;
    }

    if(tree[p].l == tree[p].r)
    {
        tree[p].x = (tree[p].x * (pri[x] - 1)) % mod;
        tree[p].tg[x] = 1;
        for(int i = 1; i <= y - 1; ++i) tree[p].x = (tree[p].x * pri[x]) % mod;
        return ;
    }

    if(tree[p].lazy != 1) pushdown(p);
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(a <= mid) updata(p<<1, a, b, x, y);
    if(mid < b) updata(p<<1|1, a, b, x, y);
    pushup(p);
}

int main()
{
    dio();
    f[1] = 1;
    for(int i = 2; i <= 100; ++i) f[i] = phi(i);

    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]);
    build(1, 1, n);
    int y, a, b, c;
    while(m--)
    {
        scanf("%d", &y);
        if(y == 1)
        {
            scanf("%d%d", &a, &b);
            printf("%lld\n", query(1, a, b) % mod);
        }
        else
        {
            scanf("%d%d%d", &a, &b, &c);
            for(int i = 1; i <= 25; ++i)
            {
                if(cnt[c][i]) updata(1, a, b, i, cnt[c][i]);
            }
        }
    }
    return 0;
}

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值