首页 > 其他分享 >【luogu CF1109E】Sasha and a Very Easy Test(线段树)

【luogu CF1109E】Sasha and a Very Easy Test(线段树)

时间:2022-09-27 10:24:59浏览次数:92  
标签:return re Very luogu zf CF1109E int tot id

Sasha and a Very Easy Test

题目链接:luogu CF1109E

题目大意

维护一个长度为 n 的序列,有区间乘,单点除(保证能整除),区间求和答案对 p 取模。
p 不一定是质数。

思路

麻了考场被卡了常,重构了一遍之后发现改时限了,考场的代码能过了。
难泵。

首先如果 \(p\) 是质数,那直接上逆元乱搞都行。(线段树)
但是不是质数,但是我们考虑直接暴力拆解,因为有问题的时候是出现倍数。

那拆出来的质数,我们维护的值就可以用一个余数和一堆次数表示。
分别是跟 \(p\) 互质的部分,以及带上了多少个某个质数的次数。

然后维护就好了。
一个小小注意的点是我们这种分解只需要在最下层进行,因为我们除法是单点的,而且是要区间求和。
所以上面的我们直接拿值来加就可以,维护最下层的 \(n\) 个即可。

代码

考场代码(比较慢,被考场的 3s 卡了时间,虽然 luogu 上很快而且考场后改成了 8s 之后我跑了 4s

#include<map>
#include<cstdio>
#include<vector>
  
using namespace std;
  
const int N = 5e5 + 100;
int n, p, q, a[N], zs[35], tot;
map <int, int> pla;
vector <int> mic[35]; int R[35];
  
int add(int x, int y) {return x + y >= p ? x + y - p : x + y;}
int dec(int x, int y) {return x < y ? x - y + p : x - y;}
int mul(int x, int y) {return 1ll * x * y % p;}
  
int re, zf; char c;
int read() {
    re = 0; zf = 1; c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') zf = -zf;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        re = (re << 3) + (re << 1) + c - '0';
        c = getchar();
    }
    return re * zf;
}
  
struct Val {
    int x, a[35];
};
  
Val operator *(Val x, Val y) {
    Val re = x; re.x = mul(re.x, y.x);
    for (int i = 0; i < tot; i++) re.a[i] += y.a[i];
    return re;
}
  
int exgcd(int a, int b, int &x, int &y) {
    if (!b) {
        x = 1; y = 1; return a;
    }
    int re = exgcd(b, a % b, y, x);
    y -= a / b * x; return re;
}
  
int get_inv(int now, int p) {
    int x, y; exgcd(now, p, x, y);
    return (x % p + p) % p;
}
  
Val operator /(Val x, Val y) {
    Val re = x; re.x = mul(re.x, get_inv(y.x, p));
    for (int i = 0; i < tot; i++) re.a[i] -= y.a[i];
    return re;
}
  
Val getVAL(int x) {
    Val re;
    re.x = x;
    for (int i = 0; i < tot; i++) {
        re.a[i] = 0;
        while (re.x % zs[i] == 0) re.a[i]++, re.x /= zs[i];
    }
    return re;
}
  
int msm(int id, int k) {//可以直接记忆化预处理次方,就不用快速幂,因为至多是大概 30*(m+1) 大概是 1e7 级别的
    while (k > R[id]) {
        mic[id].push_back(mul(mic[id][R[id]], zs[id])); R[id]++;
    }
    return mic[id][k];
}
  
int getval(Val x) {
    int re = x.x;
    for (int i = 0; i < tot; i++)
        re = mul(re, msm(i, x.a[i]));
    return re;
}
  
bool check_empty(Val x) {
    if (x.x != 1) return 0;
    for (int i = 0; i < tot; i++) if (x.a[i]) return 0;
    return 1;
}
  
struct XD_Tree {
    Val lzy[N << 2], val[N];
    int sum[N << 2], leaf[N << 2];
      
    void up(int now) {
        sum[now] = add(sum[now << 1], sum[now << 1 | 1]);
    }
      
    void downm(int now, Val a) {
        sum[now] = mul(sum[now], getval(a));
        lzy[now] = lzy[now] * a;
        if (leaf[now]) val[leaf[now]] = val[leaf[now]] * a;
    }
      
    void down(int now) {
        if (check_empty(lzy[now])) return ;
        downm(now << 1, lzy[now]); downm(now << 1 | 1, lzy[now]);
        lzy[now].x = 1; for (int i = 0; i < tot; i++) lzy[now].a[i] = 0;
    }
      
    void build(int now, int l, int r) {
        lzy[now].x = 1; for (int i = 0; i < tot; i++) lzy[now].a[i] = 0;
        if (l == r) {
            leaf[now] = l;
            val[l] = getVAL(a[l]); sum[now] = a[l] % p; return ;
        }
        int mid = (l + r) >> 1;
        build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r);
        up(now);
    }
      
    int query(int now, int l, int r, int L, int R) {
        if (L <= l && r <= R) return sum[now];
        down(now); int mid = (l + r) >> 1, re = 0;
        if (L <= mid) re = add(re, query(now << 1, l, mid, L, R));
        if (mid < R) re = add(re, query(now << 1 | 1, mid + 1, r, L, R));
        return re;
    }
      
    void times(int now, int l, int r, int L, int R, Val a) {
        if (L <= l && r <= R) {
            downm(now, a); return ;
        }
        down(now); int mid = (l + r) >> 1;
        if (L <= mid) times(now << 1, l, mid, L, R, a);
        if (mid < R) times(now << 1 | 1, mid + 1, r, L, R, a);
        up(now);
    }
      
    void inv(int now, int l, int r, int pl, Val x) {
        if (l == r) {
            val[l] = val[l] / x; sum[now] = getval(val[l]);
            return ;
        }
        down(now); int mid = (l + r) >> 1;
        if (pl <= mid) inv(now << 1, l, mid, pl, x);
            else inv(now << 1 | 1, mid + 1, r, pl, x);
        up(now);
    }
}T;
  
int main() {
    n = read(); p = read();
      
    int tmp = p;
    for (int i = 2; i * i <= tmp; i++)
        if (tmp % i == 0) {
            zs[tot++] = i; pla[i] = tot - 1;
            while (tmp % i == 0) tmp /= i;
        }
    if (tmp > 1) zs[tot++] = tmp, pla[tmp] = tot - 1;
    for (int i = 0; i < tot; i++) mic[i].push_back(1);
      
    for (int i = 1; i <= n; i++) a[i] = read();
    T.build(1, 1, n);
    q = read();
    while (q--) {
        int op = read();
        if (op == 1) {
            int l = read(), r = read(), x = read();
            T.times(1, 1, n, l, r, getVAL(x));
        }
        if (op == 2) {
            int P = read(), x = read();
            T.inv(1, 1, n, P, getVAL(x));
        }
        if (op == 3) {
            int l = read(), r = read();
            printf("%d\n", T.query(1, 1, n, l, r));
        }
//      printf("%d\n", T.val[5].x);
//      for (int i = 0; i < tot; i++) printf("%d ", T.val[5].a[i]);
//      printf("\n");
    }
      
    return 0;
}

改进代码(根据别人的写法改进了一下,考后测只需要 1s 左右

#include<map>
#include<cstdio>
#include<vector>

using namespace std;

const int N = 5e5 + 100;
int n, p, q, a[N], phi, prime[10], tot;
int tmp[10];

int add(int x, int y) {return x + y >= p ? x + y - p : x + y;}
int dec(int x, int y) {return x < y ? x - y + p : x - y;}
int mul(int x, int y) {return 1ll * x * y % p;}

int re, zf; char c;
int read() {
	re = 0; zf = 1; c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') zf = -zf;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		re = (re << 3) + (re << 1) + c - '0';
		c = getchar();
	}
	return re * zf;
}

int get_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 Init() {
	phi = get_phi(p);
	int tmp = p;
	for (int i = 2;	i * i <= tmp; i++)
		if (tmp % i == 0) {
			prime[++tot] = i; while (tmp % i == 0) tmp /= i;
		}
	if (tmp > 1) prime[++tot] = tmp;
}

int ksm(int x, int y) {
	int re = 1;
	while (y) {
		if (y & 1) re = mul(re, x);
		x = mul(x, x); y >>= 1;
	}
	return re;
}

int work(int n) {
	for (int i = 1; i <= tot; i++) {
		tmp[i] = 0; while (n % prime[i] == 0) n /= prime[i], tmp[i]++;
	}
	return n % p;
}

struct XD_tree {
	int sum[N << 2], lzy[N << 2], res[N << 2], s[N << 2][10];
	
	void up(int now) {
		sum[now] = add(sum[now << 1], sum[now << 1 | 1]);
	}
	
	void downm(int now, int val, int re, int *tmp) {
		lzy[now] = mul(lzy[now], val); sum[now] = mul(sum[now], val);
		res[now] = mul(res[now], re);
		for (int i = 1; i <= tot; i++) s[now][i] += tmp[i];
	}
	
	void down(int now) {
		if (lzy[now] != 1 || res[now] != 1) {
			downm(now << 1, lzy[now], res[now], s[now]);
			downm(now << 1 | 1, lzy[now], res[now], s[now]);
			lzy[now] = res[now] = 1;
			for (int i = 1; i <= tot; i++) s[now][i] = 0;
		}
	}
	
	void build(int now, int l, int r) {
		lzy[now] = res[now] = 1;
		if (l == r) {
			sum[now] = a[l] % p;
			res[now] = work(a[l]);
			for (int i = 1; i <= tot; i++) s[now][i] = tmp[i];
			return ;
		}
		int mid = (l + r) >> 1;
		build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r);
		up(now);
	}
	
	void times(int now, int l, int r, int L, int R, int val, int re) {
		if (L <= l && r <= R) {
			downm(now, val, re, tmp);
			return ;
		}
		down(now); int mid = (l + r) >> 1;
		if (L <= mid) times(now << 1, l, mid, L, R, val, re);
		if (mid < R) times(now << 1 | 1, mid + 1, r, L, R, val, re);
		up(now);
	}
	
	void inv(int now, int l, int r, int pl, int val) {
		if (l == r) {
			res[now] = mul(res[now], ksm(val, phi - 1));
			sum[now] = res[now];
			for (int i = 1; i <= tot; i++) s[now][i] -= tmp[i], sum[now] = mul(sum[now], ksm(prime[i], s[now][i]));
			return ;
		}
		down(now); int mid = (l + r) >> 1;
		if (pl <= mid) inv(now << 1, l, mid, pl, val);
			else inv(now << 1 | 1, mid + 1, r, pl, val);
		up(now);
	}
	
	int query(int now, int l, int r, int L, int R) {
		if (L <= l && r <= R) return sum[now];
		down(now); int mid = (l + r) >> 1, re = 0;
		if (L <= mid) re = add(re, query(now << 1, l, mid, L, R));
		if (mid < R) re = add(re, query(now << 1 | 1, mid + 1, r, L, R));
		return re;
	}
}T;

int main() {
	n = read(); p = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	Init(); T.build(1, 1, n);
	q = read();
	while (q--) {
		int op = read();
		if (op == 1) {
			int l = read(), r = read(), x = read();
			T.times(1, 1, n, l, r, x, work(x));
		}
		if (op == 2) {
			int pl = read(), x = read();
			T.inv(1, 1, n, pl, work(x));
		}
		if (op == 3) {
			int l = read(), r = read();
			printf("%d\n", T.query(1, 1, n, l, r));
		}
	}
	
	return 0;
}

标签:return,re,Very,luogu,zf,CF1109E,int,tot,id
From: https://www.cnblogs.com/Sakura-TJH/p/luogu_CF1109E.html

相关文章

  • [luogu p8251] [NOI Online 2022 提高组] 丹钓战
    [P8251NOIOnline2022提高组]丹钓战-洛谷|计算机科学教育新生态(luogu.com.cn)容易发现对于一次查询\([L,R]\),\(L\)一定是第一个入栈的,也是成功的,答案至少为......
  • luogu P1052 [NOIP2005 提高组] 过河
    [NOIP2005提高组]过河题目描述在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳......
  • luogu P2233 [HNOI2002]公交车路线
    [HNOI2002]公交车路线题目描述在长沙城新建的环城公路上一共有\(8\)个公交站,分别为A、B、C、D、E、F、G、H。公共汽车只能够在相邻的两个公交站之间运行,因此你从某一......
  • luogu P1772 [ZJOI2006] 物流运输 (dp, 最短路)
    https://www.luogu.com.cn/problem/P1772虽然是图论背景,但是1-n天之间是线性关系。没法贪心决策,考虑dp:我本来写的dp是i-1转移到i,但是这样没法处理哪一天能走哪些最短路......
  • [spring cloud] @EnabledEurekaClient 与 @EnabledDiscoveryClient的区别
    https://blog.51cto.com/u_15437298/4694156相同点都是将自身作为服务向注册中心注册不同点@EnabledEurekaClient只能向eureka注册@EnabledDiscoveryClient通用......
  • luogu P1410 子序列
    子序列题目描述给定一个长度为\(N\)(\(N\)为偶数)的序列,问能否将其划分为两个长度为\(N/2\)的严格递增子序列。输入格式若干行,每行表示一组数据。对于每组数据,首......
  • 【luogu P4218】珠宝商(SAM)(点分治)(根号分治)
    珠宝商题目链接:luoguP4218题目大意给你一棵树,每个点有一个字符。再给你一个字符串s。然后问你树上的所有简单的路径在s上的出现次数的和。思路一个比较神奇的题......
  • luogu P7632 题解
    一.思路我们可以先把时间换成以秒为单位的,然后在枚举每一秒时谁领先。二.重要点我们可以用string读入时间,再用一个函数以秒为单位提取出来(在程序中的函数名:tiqu)......
  • luogu 4025
    [PA2014]Bohater题目描述在一款电脑游戏中,你需要打败\(n\)只怪物(从\(1\)到\(n\)编号)。为了打败第\(i\)只怪物,你需要消耗\(d_i\)点生命值,但怪物死后会掉落血药......
  • luogu6927
    [ICPC2016WF]SwapSpace题面翻译你有\(n\)个硬盘\((n\leqslant10^{6})\),现在需要对所有硬盘进行格式化。格式化后,第\(i\)个硬盘的容量会由原来的\(a_{i}\)变为\(......