P14243 宇宙旅行

前言

我们队斩获了这道题的首 A,结果在我过了这道题不久后就有队伍过这道题了。

笑点解析:在过 A 后扫一眼以为 G 是最可做的题,后来发现是原赛场上只有两支队伍过的题,最后硬着头皮磕出来了。

题意

\(n\) 个非负整数 \(A_1,A_2,\cdots,A_n\),以及 \(2^{60}\) 个可重集 \(S_0,S_1,\cdots,S_{2^{60}-1}\),第 \(i\) 个可重集的第 \(j\) 个元素为 \(i\oplus A_j\)

\(q\) 次询问,每次询问给定 \(l,r,k\),要求对于所有 \(l\le i\le r\) 的可重集 \(S_i\),选择第 \(k\) 小的元素,并对这些选择出来的元素求和。

答案对 \(998244353\) 取模。

保证 \(0\le A_i<2^{60},0\le l\le r<2^{60},1\le k\le n\)

思路

容易发现询问可以被拆成两个前缀的形式,所以我们接下来只讨论如何对前缀答案。

具体的,我们接下来考虑对 \([0,r]\) 这段区间内的第 \(k\) 小值求和。


考虑只对一个可重集 \(S_i\) 我们会怎么做。
考虑对序列 \(A\) 建一棵 01-trie,并维护一棵子树内有多少个数。

然后我们从高到低按位考虑。

  • \(i\) 这一位为 \(0\),则左子树和右子树的顺序不会改变,容易判断要找的数在左子树还是右子树;
    若要找的数在右子树,则该位会产生贡献。
  • 否则相当于交换了左右子树,用类似的方法判断要找的数在左子树还是右子树即可;
    若要找的数在左子树,则该位会产生贡献。

这样一次询问是 \(O(\log V)\) 的。


再考虑对于一段前缀 \([0,r]\) 怎么做。

注意到,该位能能产生的贡献以及要找的数在哪棵子树,仅和最高位是 \(0\) 还是 \(1\) 有关。
我们按最高位把这段前缀划分成最多两个区间,而这划分出来的区间对于该位的贡献也是易求得的。

然后我们可以把该问题转化为至多两个子问题,递归地做下去即可。

最坏情况需要遍历整棵 01-trie,时间复杂度 \(O(n\log V)\)


接下来的可能需要一些注意力了。

我们发现,一段前缀若分成两段区间,必定是分成了形如 \([0,2^{lim})\)\([2^{lim},r]\) 的两个区间。

\([0,2^{lim})\) 这个区间显然是满的。
也就是说,我们每次查询都很可能重复利用到这些值。
既然如此,我们为什么不把它预处理出来呢?

我们考虑预处理为以 \(rt\) 为根时满区间查询第 \(k\) 小的答案。
我们发现在求出儿子的答案后,我们可以 \(O(size)\) 合并,方法与上文提到的差不多。

注意到每一层 01-trie 结点的 \(\displaystyle\sum size=n\),所以预处理的时空复杂度都是 \(O(n\log V)\) 的。

而每次询问我们至多选择一个子树递归下去,因此单次询问时间复杂度为 \(O(\log V)\)


总时间复杂度 \(O((n+q)\log V)\),空间复杂度 \(O(n\log V)\),可以通过此题。

程序

赛时 AC 记录

赛后 AC 记录

#include<bits/stdc++.h>
#define forUp(i,a,b) for(int i=(a);i<=(b);++i)
#define forUP(i,a,b) for(int i=(a);i<(b);++i)
#define forDown(i,a,b) for(int i=(a);i>=(b);--i)
#define forG(u,v) for(int __i=head[u],v=to[__i];__i;__i=nxt[__i],v=to[__i])
#define forWG(u,v,c) for(int __i=head[u],v=to[__i],c=w[__i];__i;__i=nxt[__i],v=to[__i],c=w[__i])
#define pb push_back
#define popc32 __builtin_popcount
#define popc64 __builtin_popcountll
bool __mst;namespace LXcjh4998{
namespace internal{namespace type_traits{
    template<class _Tp>using __is_signed_int128=typename std::conditional<std::is_same<_Tp,__int128_t>::value||std::is_same<_Tp,__int128>::value,std::true_type,std::false_type>::type;
	template<class _Tp>using __is_unsigned_int128=typename std::conditional<std::is_same<_Tp,__uint128_t>::value||std::is_same<_Tp,unsigned __int128>::value,std::true_type,std::false_type>::type;
	template<class _Tp>using __make_unsigned_int128=typename std::conditional<std::is_same<_Tp,__int128_t>::value,__uint128_t,unsigned __int128>;
	template<class _Tp>using __is_integer=typename std::conditional<std::is_integral<_Tp>::value||__is_signed_int128<_Tp>::value||__is_unsigned_int128<_Tp>::value,std::true_type,std::false_type>::type;
	template<class _Tp>using __is_signed_integer=typename std::conditional<(__is_integer<_Tp>::value&&std::is_signed<_Tp>::value)||__is_signed_int128<_Tp>::value,std::true_type,std::false_type>::type;
	template<class _Tp>using __is_unsigned_integer=typename std::conditional<(__is_integer<_Tp>::value&&std::is_unsigned<_Tp>::value)||__is_unsigned_int128<_Tp>::value,std::true_type,std::false_type>::type;
	template<class _Tp>using __to_unsigned=typename std::conditional<__is_signed_int128<_Tp>::value,__make_unsigned_int128<_Tp>,typename std::conditional<std::is_signed<_Tp>::value,std::make_unsigned<_Tp>,std::common_type<_Tp>>::type>::type;
	template<class _Tp>using __is_signed_integer_type=std::enable_if_t<__is_signed_integer<_Tp>::value>;
	template<class _Tp>using __is_unsigned_integer_type=std::enable_if_t<__is_unsigned_integer<_Tp>::value>;
	template<class _Tp>using __to_unsigned_type=typename __to_unsigned<_Tp>::type;
}using namespace type_traits;namespace check_char_type{bool __is_digit(const char &c){return 48<=c&&c<=57;}bool __is_not_blank(const char &c){return c^32&&c^10&&c^13&&c&&~c;}}using namespace check_char_type;}
namespace fastIO{
	struct istream{void tie([[maybe_unused]]int x){}}fin;struct ostream{void tie([[maybe_unused]]int x){}}fout;
	constexpr int __maxn=1<<20;char __in[__maxn],__out[__maxn],*__p1=__in,*__p2=__in,*__p3=__out;
	#define getchar() (__p1==__p2&&(__p2=(__p1=__in)+fread(__in,1,__maxn,stdin),__p1==__p2)?EOF:*__p1++)
	#define flush() (fwrite(__out,1,__p3-__out,stdout))
	#define putchar(ch) (__p3==__out+__maxn&&(flush(),__p3=__out),*__p3++=(ch))
	class __flush{public:~__flush(){flush();}}__var_to_flush;
	template<typename _Tp,internal::__is_signed_integer_type<_Tp>* =nullptr>istream& operator>>(istream &fin,_Tp &x){x=0;bool flag(false);char ch=getchar();while(!internal::__is_digit(ch))flag^=!(ch^45),ch=getchar();while(internal::__is_digit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();flag?x=-x:x;return fin;}
	template<typename _Tp,internal::__is_unsigned_integer_type<_Tp>* =nullptr>istream& operator>>(istream &fin,_Tp &x){x=0;char ch=getchar();while(!internal::__is_digit(ch))ch=getchar();while(internal::__is_digit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();return fin;}
	istream& operator>>(istream &fin,char &x){do x=getchar();while(!internal::__is_not_blank(x));return fin;}
	istream& operator>>(istream &fin,char *x){char ch=getchar();do *(x++)=ch;while(internal::__is_not_blank(ch=getchar()));return fin;}
	istream& operator>>(istream &fin,std::string &x){char ch=getchar();x.clear();do x+=ch;while(internal::__is_not_blank(ch=getchar()));return fin;}
	template<typename _Tp,internal::__is_unsigned_integer_type<_Tp>* =nullptr>ostream& operator<<(ostream &fout,_Tp x){int stk[50],tp(0);do stk[++tp]=x%10,x/=10;while(x);while(tp)putchar(stk[tp--]|48);return fout;}
	template<typename _Tp,internal::__is_signed_integer_type<_Tp>* =nullptr>ostream& operator<<(ostream &fout,_Tp x){internal::__to_unsigned_type<_Tp> tmp=x<0?(putchar(45),-x):x;fout<<tmp;return fout;}
	ostream& operator<<(ostream &fout,const char &x){putchar(x);return fout;}
	template<typename _Tp>ostream& operator<<(ostream &fout,_Tp *x){while(*x)putchar(*(x++));return fout;}
	ostream& operator<<(ostream &fout,const std::string &x){for(const char &ch:x)putchar(ch);return fout;}
	#undef getchar
	#undef putchar
	#undef flush
	#define cin LXcjh4998::fin
	#define cout LXcjh4998::fout
}using namespace fastIO;}
using uint=unsigned int;using int64=long long;using uint64=unsigned long long;using int128=__int128;using uint128=unsigned __int128;using float64=double;
constexpr int INF=0x3f3f3f3f,MINF=0xcfcfcfcf;constexpr int64 INF64=0x3f3f3f3f3f3f3f3f,MINF64=0xcfcfcfcfcfcfcfcf;constexpr float64 INFDB=1e50,eps=1e-4;
template<class _Tp>void chkMax(_Tp &x,const _Tp &y){x<y?x=y:0;}template<class _Tp>void chkMin(_Tp &x,const _Tp &y){x>y?x=y:0;}
constexpr int N=1e5+10,LEN=6e6+10,mod=998244353;int __test_num=1,__task_id,__tst;using namespace std;void __init();

int n,q;int64 A[N];

int tot=1,to[LEN][2],sz[LEN];vector<int> res[LEN];
void insert(int64 val){
	int cur=1;
	forDown(i,59,0){
		int c=val>>i&1;
		if(!to[cur][c])to[cur][c]=++tot;
		cur=to[cur][c];
		++sz[cur];
	}
}
void dfs(int rt,int64 lim){
	if(to[rt][0])dfs(to[rt][0],lim>>1);
	if(to[rt][1])dfs(to[rt][1],lim>>1);
	if(rt==1)return;
	res[rt].resize(sz[rt]+1);
	if(!to[rt][0]&&!to[rt][1])forUp(i,1,sz[rt])res[rt][i]=0;
	else{
		forUp(i,1,sz[rt]){
			if(i<=sz[to[rt][0]])res[rt][i]=(res[rt][i]+res[to[rt][0]][i])%mod;
			else res[rt][i]=(res[rt][i]+res[to[rt][1]][i-sz[to[rt][0]]]+(int128)lim*lim)%mod;
			if(i<=sz[to[rt][1]])res[rt][i]=(res[rt][i]+res[to[rt][1]][i])%mod;
			else res[rt][i]=(res[rt][i]+res[to[rt][0]][i-sz[to[rt][1]]]+(int128)lim*lim)%mod;
		}
	}
}
int query(int rt,int64 lim,int64 r,int k){
	int ans=0;
	if(!to[rt][0]&&!to[rt][1])return ans;
	if(r>=lim){
		if(k<=sz[to[rt][0]])ans=res[to[rt][0]][k];
		else ans=(res[to[rt][1]][k-sz[to[rt][0]]]+(int128)lim*lim)%mod;
		if(k<=sz[to[rt][1]])ans=(ans+query(to[rt][1],lim>>1,r-lim,k))%mod;
		else ans=(ans+query(to[rt][0],lim>>1,r-lim,k-sz[to[rt][1]])+(int128)(r-lim+1)*lim)%mod;
	}
	else{
		if(k<=sz[to[rt][0]])ans=query(to[rt][0],lim>>1,r,k);
		else ans=(query(to[rt][1],lim>>1,r,k-sz[to[rt][0]])+(int128)(r+1)*lim)%mod;
	}
	return ans;
}

bool __med;void __solve(int __test_id){
	cin>>n>>q;
	forUp(i,1,n)cin>>A[i];
	forUp(i,1,n)insert(A[i]);
	dfs(1,1ll<<59);
	while(q--){
		int64 l,r;int k;cin>>l>>r>>k;
		int ansl=l!=0?query(1,1ll<<59,l-1,k):0,ansr=query(1,1ll<<59,r,k),ans=(ansr-ansl+mod)%mod;
		cout<<ans<<'\n';
	}
}
signed main(){
	__init();
	forUp(i,1,__test_num)__solve(i);
	cerr<<1000.0*(clock()-__tst)/CLOCKS_PER_SEC<<"ms "<<((&__mst)-(&__med))/1024.0/1024.0<<"MB";
	return 0;
}
void __init(){
	__tst=clock();
	#ifndef use_file
	//#define use_file
	#endif
	#ifdef use_file
	const string __file_name="test";freopen((__file_name+".in").c_str(),"r",stdin);freopen((__file_name+".out").c_str(),"w",stdout);
	#endif
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	//scanf("%d",&__test_num);
	//cin>>__test_num;
}
posted @ 2025-10-19 19:45  LXcjh4998  阅读(3)  评论(0)    收藏  举报