首页 > 其他分享 >P9994 [Ynoi Easy Round 2024] TEST_132 题解

P9994 [Ynoi Easy Round 2024] TEST_132 题解

时间:2023-12-29 16:37:55浏览次数:20  
标签:const log int 题解 P9994 Ynoi fro return mod

题解怎么都是用暴力日过去的啊。

思路

考虑根号分治,设阈值为 \(B\)。

对于第二维出现次数超过 \(B\) 的,我们可以在修改时暴力更改,这部分复杂度为 \(O(\frac{nm}{B})\)。

对于第二维出现次数小于 \(B\) 的,我们可以在修改是打标记,查询时遍历一遍,这部分的复杂度为 \(O(mb)\)。

大多数题解对出现次数小于 \(B\) 的处理都没有达到严格的线性,而是使用了快速幂进行计算,这里给一种线性做法。

对于所有的数,我们把他们的离散对数算出来,在模 \(1000000007\) 意义下就是算 \(\log_5\)。

那么我们可以预处理 \(5\) 的光速幂,在查询的时候可以 \(O(1)\) 计算答案。

而在修改时,对原数平方相当于对离散对数乘二。

同样可以预处理二的次幂来做到。

问题就变成了在最开始计算每个数的离散对数。

首先可以使用 BSGS,我们预处理前 \(5\times 10^6\) 次方项,每次查询就只需要遍历 \(\frac{mod}{5\times 10^6}\) 约 \(200\) 下。

这样在复杂度上就已经非常优越了。

但在实现上,由于 BSGS 与哈希表(即使是手写哈希表)常数过大。

所以跑 \(10^6\) 次 BSGS 是非常慢的。

甚至表现的结果不如快速幂(当然这也与数据有关)。

怎么办呢。

考虑一种 BSGS 次数更少的方法。

对 \(mod\) 作带余除法 \(mod=ax+b(a,b\in Z,0\le b<x)\),有:

\[mod=(a+1)x+(r-x) \]

于是可以得到:

\[\log x≡\log(-b)-\log a≡\log(x-b)-\log(a+1)\pmod{φ(p)} \]

若我们已经预处理出 \(≤ \sqrt{mod}\) 范围内的所有数的离散对数,那么只需考虑 \(x > \sqrt{mod}\) 的情况。

此时 \(a = \frac{mod}{x} < \sqrt{mod}\),因此 \(a\), \(a + 1\) 的离散对数也是已知的。

于是,我们可以根据上面两条式子,将计算 \(\log x\) 的问题递归到计算 \(\log b\) 或 \(\log(x - b)\) 的问题。

由于 \(\min(b, x - b) \le \frac{x}{2}\),就可以在 \(O(\log x)\) 的时间内计算 \(x\) 的离散对数。

我们也只需要执行 \(\sqrt{mod}\) 次 BSGS 算出前根号项即可。

这样常数大大减少,就可以完全不卡常的过了。

所以正解怎么可能会卡常呢。

Code

阈值 \(B\) 设为 \(500\)(因为光速幂非常龟速)。

这是完全没卡常的代码,在总时间可能没有快速幂快。

但最慢点只要 \(3s\)。

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

#define x first
#define y second
#define eb(...) emplace_back(__VA_ARGS__)
#define fro(i, x, y) for(int i = (x);i <= (y);i++)
#define pre(i, x, y) for(int i = (x);i >= (y);i--)

typedef int64_t i64;
typedef pair<int, int> PII;

const int B = 500;
const int D = 5e6;
const int P = 4e4;
const int I = 131072;
const int M = 2935019;
const int N = 1e6 + 10;
const int G = 5e6 + 10;
const int mod = 1e9 + 7;
const int phi = 1e9 + 6;
const int lgf = 5e8 + 3;

int n, m;	
int sum[N], tag[N], ton[N];
int a[N], b[N], v[N], g[N], d[N];
vector<PII> big[N], sml[N];
i64 inv, pw1[N], pw2[N], pw3[N];
struct HashMap
{
	int cnt, head[M];
	struct edge { int to, val, nxt; } e[G];
	inline int &operator[](const int &tmp)
	{
		int x = tmp % M;
		for(int i = head[x]; i; i = e[i].nxt)
			if(e[i].to == tmp) return e[i].val;
		e[++cnt] = {tmp, 0, head[x]}, head[x] = cnt;
		return e[cnt].val;
	}
	inline bool find(const int &tmp)
	{
		int x = tmp % M;
		for(int i = head[x]; i; i = e[i].nxt)
			if(e[i].to == tmp) return 1;
		return 0;
	}
} mp;

inline i64 power(int x)
	{ return pw1[x&(I-1)] * pw2[x>>17] % mod; }
inline i64 power(i64 x, i64 y)
{
	i64 res = 1;
	while(y)
	{
		if(y & 1) res = res * x % mod;
		x = x * x % mod, y /= 2;
	}
	return res;
}
inline void init()
{
	pw1[0] = pw2[0] = pw3[0] = 1; i64 pw = 1;
	fro(i, 1, I) pw1[i] = pw1[i - 1] * 5 % mod;
	fro(i, 1, I) pw2[i] = pw2[i - 1] * pw1[I] % mod;
	fro(i, 1, N - 10) pw3[i] = pw3[i - 1] * 2 % phi;
	fro(i, 0, D) mp[pw] = i, pw = pw * 5 % mod;
	inv = power(power(D), mod - 2);
	auto ask = [&](int x) {
		fro(i, 0, 200)
		{
			if(mp.find(x))
				return i * D + mp[x];
			x = x * inv % mod;
		}
		return -1;
	};
	fro(i, 1, P) d[i] = (i % 5 == 0 ? d[i / 5] + 1 : ask(i));
}
inline void add(int &x, int y, int M = mod)
	{ x = (x + y >= M ? x + y - M : x + y); }
inline int Add(int x, int y)
	{ return (x + y >= phi ? x + y - phi : x + y); }
inline int ask(int x)
{
	if(x <= P) return d[x];
	int q = mod / x, r = mod % x;
	if(r < x - r)
		return Add(Add(lgf, ask(r)), phi - d[q]);
	return Add(ask(x - r), phi - d[q + 1]);
}
inline void solve()
{
	cin >> n >> m, init();
	fro(i, 1, n) cin >> a[i] >> b[i] >> v[i];
	fro(i, 1, n) g[i] = ask(v[i]);
	fro(i, 1, n) ton[b[i]]++;
	fro(i, 1, n)
	{
		if(ton[b[i]] <= B) sml[b[i]].eb(a[i], g[i]);
		if(ton[b[i]] >  B) big[a[i]].eb(b[i], v[i]), add(sum[b[i]], v[i]);
	}
	fro(i, 1, m)
	{
		int op, x;
		cin >> op >> x;
		if(op == 1)
		{
			for(auto &[a, b] : big[x])
			{
				add(sum[a], mod - b);
				b = 1ll * b * b % mod;
				add(sum[a], b);
			}
			tag[x]++;
		}
		else
		{
			if(ton[x] > B)
				cout << sum[x] << "\n";
			else
			{
				int res = 0;
				for(auto [a, b] : sml[x])
				{
					int pos = b * pw3[tag[a]] % phi;
					add(res, power(pos));
				}
				cout << res << "\n";
			}
		}
	}
}

signed main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	solve();
	return 0;
}

标签:const,log,int,题解,P9994,Ynoi,fro,return,mod
From: https://www.cnblogs.com/JiaY19/p/17935161.html

相关文章

  • P9995 [Ynoi2000] rspcn 题解
    思路比较典的ODT题目。发现排序是一个非常有性质的操作。它对区间的更改与颜色段均摊差不多。那么我们可以想到用ODT来维护这一整个序列。具体的,区间排序操作可以用ODT维护每次排序产生的段,每段用线段树维护排序后的结果。每次修改就可以进行线段树的分裂与合并。如......
  • P9991 [Ynoi Easy Round 2023] TEST_107 题解
    思路题目即要求删除区间中的一个或多个颜色。考虑假如枚举删除颜色\(k\)。那么在\(l,r\)中的答案为:\[\max_{i=1}^{m+1}a_i-a_{i-1}\]其中\(a_i\)为颜色\(k\)在\(l\simr\)中的出现位置,\(a_{0}=l,a_{m+1}=r\)。可以分类讨论。答案为\(a_1-l\),那么只需要维护\(......
  • AT_abc020_c 题解
    链接(atcoder)链接(luogu)简单算法组合(?算法一爆搜,时间复杂度\(O(2^{n\timesm}\timest)\),不能通过此题。算法二考虑二分\(t\),然后暴搜,时间复杂度\(O(2^{n\timesm}\timeslog2(t))\),不能通过此题。算法三考虑二分\(t\),然后暴记忆化搜索,时间复杂度\(O(n\timesm......
  • CF1234F 题解
    blog。小清新题,下文\(V=20\)即值域。反转操作,本质就是选两个不相交连续段拼起来。显然合法的最终串长度一定\(\leV\)。将这些合法串预处理出来,那么每个串都对应一个「字母集合」。随便DP一下,求出所有集合中,的最大的合法「字母集合」大小。\(dp_{\smallU}\)就是只选一......
  • CF1917F Construct Tree 题解
    题目链接:https://codeforces.com/contest/1917/problem/F题意有\(n\)条长度\(l_i\)的边,问它们是否能组成一棵\(n+1\)个节点的树,使得树的直径长度为\(d\)。\(n,d\le2000\)。题解首先当然要存在一个边集\(D\),使得\(\sum\limits_{i\inD}l_i=d\),这可以使用背包......
  • 在不使用内置函数和中间变量的情况交换数字LeetCode力扣题解面试题16.01
    #异或法#Kotlin```KotlinclassSolution{  funswapNumbers(numbers:IntArray):IntArray{    numbers[0]=numbers[0]xornumbers[1]    numbers[1]=numbers[1]xornumbers[0]    numbers[0]=numbers[0]xor......
  • AT_joisc2015_h 题解
    传送门题意:给定长为\(n\)的字符串\(s\),你可以选择一个区间,将区间内的字符从小到大排序,求可以得到的最长回文子串长度,字符集大小为\(n\)。很有意思的题目。首先容易做到\(O(n^3)\)。考虑怎么优化。我们先考察排序的区间和回文区间的关系。如果两个区间无交,那么显然排序......
  • CF1835C Twin Clusters 题解
    题目链接点击打开链接题目解法很牛逼的构造题好像随也可以过长度为\(2^{k+1}\)的序列的不同区间有\(2^{2k+1}\)个,值域是\(4^k\),所以一定有解将\(a\)做一遍前缀和,那么问题转化成了找\(s_{r1}\opluss_{l1-1}=s_{r2}\opluss_{l2-1}\)先讲一讲具体做法:按照前\(k\)......
  • ICPC2021Kunming G Glass Bead Game 题解
    QuestionICPC2021KunmingGGlassBeadGame有\(n\)个玻璃珠,\(B_1,B_2,\cdots,B_n\)每一步你可以选择一个\(B_i\)移道第一个位置上,花费的代价为操作前\(B_i\)前面的玻璃珠的个数。已知每一步选择玻璃珠\(B_i\)的概率\(p_i\),问当\(m\rightarrow\infty\)时,在第\(......
  • ICPC2021Kunming G Find the Maximum 题解
    QuestionFindtheMaximum给出一个树,每个点有一个权值\(b_n\),求一条树上路径\(V\),要求\(\frac{\sum_{u\inV(-x^2+b_ux)}}{|V|}\)最大,其中\(x\)是自己选择的一个树Solution先转化一下\(\frac{\sum_{u\inV(-x^2+b_ux)}}{|V|}\),得到\[\frac{\sum_{u\inV(-x^2+b_......