P9994 [Ynoi Easy Round 2024] TEST_132 题解

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

思路#

考虑根号分治,设阈值为 B

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

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

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

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

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

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

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

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

首先可以使用 BSGS,我们预处理前 5×106 次方项,每次查询就只需要遍历 mod5×106200 下。

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

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

所以跑 106 次 BSGS 是非常慢的。

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

怎么办呢。

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

mod 作带余除法 mod=ax+b(a,bZ,0b<x),有:

mod=(a+1)x+(rx)

于是可以得到:

logxlog(b)logalog(xb)log(a+1)(modφ(p))

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

此时 a=modx<mod,因此 a, a+1 的离散对数也是已知的。

于是,我们可以根据上面两条式子,将计算 logx 的问题递归到计算 logblog(xb) 的问题。

由于 min(b,xb)x2,就可以在 O(logx) 的时间内计算 x 的离散对数。

我们也只需要执行 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;
}

作者:JiaY19

出处:https://www.cnblogs.com/JiaY19/p/17935161.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   JiaY19  阅读(16)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示