Loading

P5330 [SNOI2019] 数论 - 数论

题意

给定 \(p,q,T\) 和正整数集 \(A,B\),求:

\[\sum_{i=0}^{T-1} [(i\bmod p)\in A\land (i\bmod q)\in B] \]

\(1\le p,q\le 10^6,1\le T\le 10^{18}\)

题解

这题代码应该挺简单啊,为啥我就是写不出来。

\(p>q\) 则会出现多个 \(a_i\) 在模 \(q\) 意义同余的情况,此时要交换 \(p,q\)\(A,B\)

我们建一张 \(q\) 个点的图,编号 \(0\dots (q-1)\)。对于点 \(i\),我们从 \(i\)\((i+p)\bmod q\) 连边。那么图中会有若干个环。显然,每个环内的数都是 \((a+bp)\bmod q\) 的形式,且相同的环内的数有相同的 \(a\)

我们枚举 \(A\) 中的元素。对于元素 \(x\),我们需要统计出有多少非负整数 \(k\) 使得 \(x+kp<T\)\((x+kp)\bmod q\in B\)。这与图的形式相同,所以满足条件的 \(x+kp\) 就是 \(x\) 所在的环上的一段区间(这个区间可能多次包含某个点)。我们计算出这个区间会完整地覆盖这个环多少次,再加上零散部分的值即可。零散部分有多少满足 \(\in B\) 的点,这个信息可以预处理环上的前缀和得到。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
typedef long long ll;
const int V=1e6+5;
int p,q,n,m,a[V],b[V],in[V],pos[V];
ll T,val[V];
int col[V],blcnt;
vector<int> bl[V];
void Dfs(int u){
	if(col[u]) return;
	col[u]=blcnt,bl[blcnt].push_back(u),pos[u]=bl[blcnt].size()-1;
	val[blcnt]+=in[u];
	Dfs((u+p)%q);
}
vector<ll> s[V];
ll Count(int ring,int x,int d){
	if(!d) return 0;
	return s[ring][pos[x]+d-1]-(pos[x]?s[ring][pos[x]-1]:0);
}
int main(){
	ios::sync_with_stdio(false),cin.tie(nullptr);
	cin>>p>>q>>n>>m>>T;
	For(i,1,n) cin>>a[i];
	For(i,1,m) cin>>b[i];
	if(p>q) swap(p,q),swap(a,b),swap(n,m);
	For(i,1,m) in[b[i]]=1;
	For(i,0,q-1) if(!col[i]) ++blcnt,Dfs(i);
	For(i,1,blcnt){
		s[i].push_back(in[bl[i].front()]);
		for(auto it=next(bl[i].begin());it!=bl[i].end();++it){
			s[i].push_back(s[i].back()+in[*it]);
		}
		for(auto it=bl[i].begin();it!=bl[i].end();++it){
			s[i].push_back(s[i].back()+in[*it]);
		}
	}
	ll ans=0;
	For(i,1,n){
		ll lim=(T-1-a[i])/p+1,len=bl[col[a[i]]].size();
		if(T-1-a[i]>=0) ans+=Count(col[a[i]],a[i],lim%len)+lim/len*val[col[a[i]]];
	}
	cout<<ans;
	return 0;
}
posted @ 2021-12-29 21:34  Alan_Zhao_2007  阅读(41)  评论(0编辑  收藏  举报