LOJ #3096. 「SNOI2019」数论 脑洞+循环节

碰到这种题第一反应就是找循环节.            

我们发现我们就是要求 $a[i]+P \times k=b[i] ( \mod Q)$ 中 $P$ 的 k 的个数.  

那么对于 $a[i]$ 来说,最大步数为 $\frac{T-1-a[i]}{P}$.    

而我们发现 $a[i]+ P \times k$ 的循环节是 $P \times k=0( \mod Q)$,这个循环节是 $lcm(P,Q)$,而 $k$ 的个数就是 $\frac{lcm(P,Q)}{P}$ 

枚举 $0$ ~ $Q$,然后对于 $i$ 向 $(i+P)%Q$ 连边,一定会形成若干个环长为 $\frac{Q}{gcd(P,Q)}$ 的环. 

那么,让每一个 $a[i]$ 在环上走 $\frac{T-1-a[i]}{P}$ 步,算一下点权和即可. 

code:  

#include <bits/stdc++.h>         
#define ll long long 
#define N 1000006 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;   
ll T,ans,p[N];     
vector<int>s[N],v[N];    
int len,P,Q,n,m,a[N],b[N],tx[N],w[N],col[N],pos[N],num;        
int dfs(int x,int c) 
{
    if(col[x])   
        return 0;   
    col[x]=c;
    v[col[x]].push_back(x);  
    return tx[x]+dfs((x+P)%Q,c);    
}    
int find(int l,int x) 
{
    return s[col[x]][pos[x]+l]-s[col[x]][pos[x]];   
}
int main() 
{ 
    // setIO("input");    
    int i,j;          
    scanf("%d%d%d%d%lld",&P,&Q,&n,&m,&T);      
    for(i=1;i<=n;++i)   
        scanf("%d",&a[i]);    
    for(i=1;i<=m;++i)  
        scanf("%d",&b[i]);    
    if(P>Q) 
    {
        swap(P,Q); 
        swap(a,b); 
        swap(n,m);      
    } 
    len=Q/__gcd(P,Q);
    for(i=1;i<=m;++i)    
        tx[b[i]]=1;    
    for(i=1;i<=n;++i)   
        p[i]=(T-1-a[i])/P;   
    for(i=0;i<Q;++i) 
    {
        if(!col[i])    
            ++num,w[num]=dfs(i,num);   
    }     
    for(i=1;i<=num;++i) 
    {
        for(j=0;j<v[i].size();++j)   
            pos[v[i][j]]=j; 
        int t=v[i].size()-1;     
        for(j=0;j<t;++j)   
            v[i].push_back(v[i][j]);    
        s[i].push_back(tx[v[i][0]]);      
        for(j=1;j<v[i].size();++j)   
            s[i].push_back(s[i][j-1]+tx[v[i][j]]);          
    }  
    for(i=1;i<=n;++i) 
    {
        ans+=(ll)(p[i]/len)*w[col[a[i]]];       
        ans+=find(p[i]%len,a[i])+tx[a[i]];     
    }
    printf("%lld\n",ans);  
    return 0; 
}

  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值