[SNOI2019]数论
前几天我们NOIP模拟赛的最后一题,然后一不小心挂了 $ 10 $ 分,痛失ak。
题目链接:luoguP5330 。
题目大意:给定两个数 $ P,Q $ 和两个集合 $ A,B $ 求 $ \sum\limits_{i=0}^{T-1}[(i\bmod P\in A)\wedge(i\bmod Q\in B)] $ 。
首先我们来看一个显而易见的规律:
假如说 $ P=4,Q=6 $。
$ 0\bmod P=0,0\bmod Q=0 $
$ 1\bmod P=1,1\bmod Q=1 $
$ \dots $
$ 11\bmod P=3,11\bmod Q=5 $
$ 12\bmod P=0,12\bmod Q=0 $
$ 13\bmod P=1,13\bmod Q=1 $
我们会发现,数字对于 $ P,Q $ 取模的值构成了“循环”,并且所有这些循环,对答案的贡献是相同的。
这些循环的答案非常容易计算,并且我们还可以知道一个循环的大小为 $ \dfrac{PQ}{\gcd(P,Q)} $ 。
这就是当 $ T $ 为 $ P,Q $ 公倍数时的解法。
配合暴力,我们可以拿到 $ 40 $ 分。
现在我们可以快速处理一个完整的循环,但是如果最后数字的个数不足以构成循环,我们就只能暴力。
显然,这样时间复杂度是 $ O(PQ) $ 的。
我们再来找规律,把数字 $ \bmod P,Q $ 的值做成表格。
$ P=4,Q=6: $
%P\%Q的值 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0,12 | 8,20 | 4,16 | |||
1 | 1,13 | 9,21 | 5,17 | |||
2 | 6,18 | 2,14 | 10,22 | |||
3 | 7,19 | 3,15 | 11,23 |
$ P=5,Q=7: $
%P\%Q的值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
0 | 0,35 | 15,50 | 30,65 | 10,45 | 25,60 | 5,40 | 20,55 |
1 | 21,56 | 1,36 | 16,51 | 31,66 | 11,46 | 26,61 | 6,41 |
2 | 7,42 | 22,57 | 2,37 | 17,52 | 32,67 | 12,47 | 27,62 |
3 | 28,63 | 8,43 | 23,58 | 3,38 | 18,53 | 33,68 | 13,48 |
4 | 14,49 | 29,64 | 9,44 | 24,59 | 4,39 | 19,54 | 34,69 |
发现什么了吗?
对于第一个表格,$ \bmod P=0 $ 的数字分别是 $ 0,4,8,12,16,20 $ ,这些数字 $ \bmod Q $ 的值分别是 $ 0,4,2,0,4,2 $ 。
$ \bmod P=1 $ 的数字分别是 $ 1,5,9,13,17,21 $ ,这些数字 $ \bmod Q $ 的值分别是 $ 1,5,3,1,5,3 $ 。
有没有发现什么?
再看一眼第二个表格。
$ \bmod P=0 $ 的数字分别是 $ 0,5,10,15,20,25,30,35,40,45,50,55,60,65 $ ,$ \bmod Q $ 的值分别是 $ 0,5,3,1,6,4,2,0,5,3,1,6,4,2 $ 。
$ \bmod P=1 $ 的数字分别是 $ 1,6,11,16,21,26,31,36,41,46,51,56,61,66 $ ,$ \bmod Q $ 的值分别是 $ 1,6,4,2,0,5,3,1,6,4,2,0,5,3 $ 。
发现了什么呢?
假如说上一个出现的数字 $ \bmod Q $ 的值是 $ x $ ,那么下一个值就是 $ (x+P)\bmod Q $ 。
好,现在让我们把这个序列写出来,但是这个序列中可能不包含 $ 0\sim Q-1 $ 中的所有数,所以我们把若干个这样的序列“拼”在一起。
举个例子,对于第二个表格,对应的序列就是 $ 0,5,3,1,6,4,2 $ ,对于第一个表格,对应的序列就是 $ 0,4,2,1,5,3 $ 。
然后呢?不难发现,对于某一个 $ \bmod P $ 的值,他对答案的贡献在这个序列中是完整的一段。
然后前缀和就可以了。
前缀和维护的是 $ 0~Q-1 $ 的这个排列中,前 $ i $ 个数字里面,有多少个属于集合 $ B $。
所以可以直接枚举 $ A $ 中的元素,计算它的贡献。
我考场上傻了,写了个线段树。
时间复杂度 $ O(P+Q) $ 。
我写的是 $ O(Q+P\log Q) $ 。
代码:
// DABC ABCD ABCA DBAA
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<map>
using namespace std;
using pi=pair<int,int>;
using ll=long long;
using pl=pair<ll,ll>;
using pli=pair<ll,int>;
template<typename A>
using vc=vector<A>;
using vi=vector<int>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline ll lread()
{
ll s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int gcd(int a,int b)
{
int r=a%b;
while(r)
{
a=b;
b=r;
r=a%b;
}
return b;
}
bool pp[1000001];
bool qq[1000001];
int t1[4000001];
int val[1000001];
int wh[1000001];
int visa[1000001];
int visb[1000001];
int a[1000001];
int b[1000001];
int n,m,p,q;
int tot;
ll ans;
ll t;
void add(int p,int pl,int pr,int x)
{
t1[p]++;
if(pl==pr) return ;
int mid=(pl+pr)>>1;
if(x<=mid) add(p*2,pl,mid,x);
else add(p*2|1,mid+1,pr,x);
}
int get(int p,int pl,int pr,int l,int r)
{
if(l<=pl&&pr<=r) return t1[p];
int mid=(pl+pr)>>1,ans=0;
if(l<=mid) ans+=get(p*2,pl,mid,l,r);
if(mid<r) ans+=get(p*2|1,mid+1,pr,l,r);
return ans;
}
int main()
{
p=read(),q=read(),n=read(),m=read(),t=lread();
int num=gcd(p,q);ll round=(ll)p*q/num;
for(int i=1;i<=n;i++) a[i]=read(),visa[a[i]%num]++,pp[a[i]]=1;
for(int i=1;i<=m;i++) b[i]=read(),visb[b[i]%num]++,qq[b[i]]=1;
for(int i=0;i<num;i++) ans+=(ll)visa[i]*visb[i]*(t/round);
ll rest=t-(t/round)*round;
for(int i=0;i<num;i++)
{
for(int j=i;;)
{
val[++tot]=j;
wh[j]=tot;
j=(j+p)%q;
if(j==i) break;
}
}
// for(int i=1;i<=q;i++) printf("%d ",val[i]);
// printf("\n");
for(int i=1;i<=m;i++) add(1,1,q,wh[b[i]]);
for(int i=1;i<=n;i++)
{
int v=a[i];//第v行
if(rest<i) continue;
int step=rest/p;
if(v<rest%p) step++;
if(!step) continue;
int l=wh[v%q],r=wh[v%q]+step-1;
// printf("%d : %d %d ",a[i],l,r);
l--,r--;
// ll mem=ans;
if(l/(q/num)==r/(q/num)) ans+=get(1,1,q,l+1,r+1);
else
{
int l2=l/(q/num)*(q/num);
int r2=l2+q/num-1;
ans+=get(1,1,q,l2+1,r-(q/num)+1);
ans+=get(1,1,q,l+1,r2+1);
// printf("%d %d %d %d ",l2+1,r-(q/num)+1,l,r2);
}
// printf("%lld\n",ans-mem);
}
printf("%lld\n",ans);
return 0;
}
感谢观看!
本文来自博客园,作者:Wuyanru,转载请取得作者同意并注明原文链接:https://www.cnblogs.com/Wuyanru/p/SNOI-Number_Theory.html
本文内容如有侵权行为,请联系作者删除,qq:1395170470