AT_jsc2022_final_e Circular Sushi 题解

AT_jsc2022_final_e Circular Sushi

\(a_i,b_i,c_i\) 表示起始位置,速度,和价值。我们先考虑 \(t\) 只为整数。对于每一个 \(i\),考虑合法的 \(t\),有 \(a_i+b_it\equiv0\pmod{2^m}\),显然 \(t\equiv-\frac{a_i}{b_i}\pmod{2^m}\),但由于逆元可能不存在不可以直接这么做。

同余相关问题有一个经典套路可以解决这个问题:把分母按照和模数的公因数分解,剩下的部分一定存在逆元。具体的,设 \(b_i=2^{d_i}e_i\),且 \(e_i\) 中不包含因数 \(2\),则 \(e_i\) 在模 \(2^m\) 条件下一定存在逆元 \(e_i^{-1}\),因此原式可以化为 \(2^{d_i}t\equiv-a_ie_i^{-1}\pmod{2^m}\)

一个非常基础的结论是,对于同余方程 \(a\equiv b\pmod{p}\),若 \(g\mid a,g\mid b,g\mid p\),则有 \(\frac{a}{g}\equiv \frac{b}{g}\pmod{\frac{p}{g}}\)。这个除法和乘逆元不一样,这里的除法不是模意义下除法,而是正常的整数除法,我因为把这个和乘逆元弄混了导致做不出此题。

由于我们假设了 \(t\) 是整数,所以 \(2^m\mid(-a_ie_i^{-1})\),因此可以左右和模数同时除以 \(2^m\) 得到 \(t\equiv-\frac{a_ie_i^{-1}}{2^{d_i}}\pmod{2^{m-d_i}}\)

我们得到的 \(t\) 是模意义下的值,由于不同的 \(t\) 模数不一样,不能直接比较相等。考虑模 \(2^k\) 相当于取 \(t\) 的二进制从 \(0\) 开始的低 \(k\) 位,且我们从低位到高位考虑每次只需要在前几位的限制下考虑取模等于这一位取 \(0\) 还是取 \(1\),启发我们在 Trie 树上走路。

记第 \(i\) 个寿司算出来的 \(t\)\(t_i\)。先将所有 \(t_i\) 转化为最小正值,维护一个根节点为第 \(0\) 层的 Trie 树,从低位到高位插入每一个 \(t_i\),并将这一个 \(t_i\) 对应的 \(c_i\) 累加在第 \(m-d_i\) 层走到的点。最终的答案为从根节点走到每一个叶子节点的路径上的点权和的最大值。

由于 \(t_i\) 的贡献被挂在了第 \(m-d_i\) 层,所以之后这些位如何选择都可以选上这个 \(t_i\),这正是只考虑了 \(t_i\)\(2^{m-d_i}\) 的值。另一方面,每一位的决策会使这一位不同的 \(t_i\) 不在同一条路径,对应了模意义下低位不相等的 \(t_i\) 不能同时选。因此这个做法可以解决 \(t_i\) 均为整数的问题。

考虑 \(t\) 不为整数,常用的转化方式是把所有 \(t\) 乘上一个数使得它们都变为整数。设 \(a_i=2^{k_i}a'_i\),则 \(2^{k_i}a'_i+2^{d_i}e_it\equiv0\pmod{2^m}\),左右和模数同时除以 \(2^{k_i}\)\(a'_i+2^{d_i-k_i}e_it\equiv0\pmod{2^{m-k_i}}\),整理得 \(t\equiv-a'_ie_i^{-1}(2^{d_i-k_i})^{-1}\pmod{2^{m-k_i}}\)。注意到 \(d_i\le m\),且 \(k_i\ge 0\),则 \(m-d_i+k_i\ge 0\),而 \(a'_i,e_i^{-1}\) 在模意义下都为整数,因此 \(-a_i'e_i^{-1}2^{m-d_i+k_i}\) 为整数,这也证明了所有 \(t\) 乘以 \(2^m\) 后均为整数。

考虑如何把同余式左右两边同时乘以 \(2^m\),因为直接乘的话会导致左右两边都在模意义下变为 \(0\)。于是我们发现另一个很基础的性质:对于同余方程 \(a\equiv b\pmod{p}\) 和正整数 \(g\),有 \(ag\equiv bg\pmod{pg}\)。于是,我们可以得到 \(2^mt\equiv-a_i'e_i^{-1}2^{m-d_i+k_i}\pmod{2^{2m-k_i}}\)。我们把 \(2^mt\) 视作一个寿司的 \(t_i\),再按照 \(t\) 为整数时的做法在 Trie 树上做即可。

需要注意这里的逆元需要使用扩展欧几里得算法求出,因为模数不是质数。时间复杂度 \(O(n\log n+nm)\)

#include <bits/stdc++.h>
using namespace std;
long long n,m,a[300000],b[300000],c[300000],tr[12000000][2],mx[12000000],f[12000000],cnt=0;
void insert(long long c,long long dep,long long v)
{
	long long x=0;
	for(int i=0;i<=dep-1;i++)
	    {
	    	int id=(c>>i)&1;
	    	if(!tr[x][id])tr[x][id]=++cnt;
	    	x=tr[x][id];
		}
	mx[x]+=v;
}

void dfs(long long x)
{
	if(tr[x][0])dfs(tr[x][0]);
	if(tr[x][1])dfs(tr[x][1]);
	f[x]=max(f[tr[x][0]],f[tr[x][1]])+mx[x];
}

long long exgcd(long long a,long long b,long long &x,long long &y)
{
	if(b==0)
	   {
	   x=1,y=0;
	   return a;
       }
	long long g=exgcd(b,a%b,x,y),d=x;
	x=y,y=d-a/b*y;
	return g; 
}

long long inv(long long v,long long mod)
{
	long long x=0,y=0;
	exgcd(v,mod,x,y);
	return x;
}

int main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)
	    {
		scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
		long long k=0,d=0,na=a[i],nb=b[i];
		while(na&&na%2==0)k++,na>>=1;
		while(nb&&nb%2==0)d++,nb>>=1;
		const long long mod=(1ll<<(2*m-d));
		long long t=mod-1ll*na*(1ll<<(m+k-d))%mod*inv(nb,mod)%mod;
		insert(t,2*m-d,c[i]);
		}
	dfs(0);
	printf("%lld\n",f[0]);
	return 0;
}
posted @ 2026-03-21 21:04  w9095  阅读(12)  评论(0)    收藏  举报