把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5330】[SNOI2019] 数论(数论)

点此看题面

  • 给定一个大小为\(n\)的整数集\(A\),一个大小为\(m\)的整数集\(B\)以及三个数\(P,Q,T\)
  • \(\sum_{i=0}^{T-1}[(i\% P\in A)\wedge(i\% Q\in B)]\)
  • \(n,m\le10^6\)

数论+建图

考虑枚举\(a_i\in A\),去计算有多少个\((kP+a_i)\% Q\in B\)

发现\(P\)固定,每次\(k\)增加\(1\),从一个\(x=(kP+a_i)\%Q\)会变成的\(y=((k+1)P+a_i)\%Q\)是始终不变的。

于是我们建出一张图,从每个\(x\)\((x+P)\%Q\)连一条边,给所有\(b_i\)打上标记,则问题就变成从\(a_i\)出发走\(\lfloor\frac{T-1-a_i}{P}\rfloor\)步能经过多少关键点。

显然,这张图由若干个环构成,对于每个环记下它的长度以及标记总和,并任选一个点把环断开依次记下环上的每个点,求出标记的前缀和。同时,记录每个点所在环及在环中的位置。

那么从一个点\(x\)出发走\(k\)步,假设所在环长为\(cnt\),所在位置为\(rk\)(位置标号为\(0\sim cnt-1\)),它将把整个环遍历\(\lfloor\frac k{cnt}\rfloor\)次,并额外走过\(rk\sim (rk+k)\%cnt\)的所有点,后者利用我们预处理出的标记前缀和可以方便计算。

代码:\(O(P+Q)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define LL long long
using namespace std;
int n,m,P,Q,a[N+5],b[N+5];LL T;
int id[N+5],rk[N+5],s[N+5],cnt[N+5],tot[N+5];vector<int> w[N+5];
int main()
{
	RI i,j;for(scanf("%d%d%d%d%lld",&P,&Q,&n,&m,&T),i=1;i<=n;++i) scanf("%d",a+i);
	for(i=1;i<=m;++i) scanf("%d",b+i),s[b[i]]=1;//打标记
	RI x,y,c=0;for(i=0;i^Q;++i) if(!id[i])
	{
		x=i,++c;W(!id[x]) w[id[x]=c].push_back(x),rk[x]=cnt[c]++,x=(x+P)%Q;//遍历环,依次记录所有点
		for(j=1;j^cnt[c];++j) s[w[c][j]]=s[w[c][j]]+s[w[c][j-1]];tot[c]=s[w[c][cnt[c]-1]];//求出标记的前缀和,记录整个环的标记总和
	}
	LL k,t=0;for(i=1;i<=n;++i) k=(T-1-a[i])/P,x=id[a[i]%Q],y=rk[a[i]%Q],//从第x个环的第y位出发走k步
		t+=k/cnt[x]*tot[x],k%=cnt[x],t+=(y+k<cnt[x]?s[w[x][y+k]]:tot[x]+s[w[x][y+k-cnt[x]]])-(y?s[w[x][y-1]]:0);//遍历整环k/cnt[x]遍,额外走y~(y+k)%cnt[x]所有点
	return printf("%lld\n",t),0;
}
posted @ 2021-06-16 16:50  TheLostWeak  阅读(63)  评论(0编辑  收藏  举报