HDU

来源:互联网 发布:有道云笔记数据恢复 编辑:程序博客网 时间:2024/06/03 22:40

题目链接


题意:


对于给出的区间[x, y]找出有多少个符合要求的数:

  1. 能被7整除.
  2. 给出不超过15组(pi, ai),其中pi为质数;
    要求找出的数x满足 x % pi != ai;
思路:

   按照数位dp那一顿怼啊,后来发现好像不大对啊。

我们知道如果给你很多(pi,ai)让你找出最小的x%pi = ai。也就是解同余方程,我们肯定就知道是CRT了.

        那么这个题目给我们只有15组,还告诉你pi乘积小于1e18,不爆ll,也在提醒我们CRT吧.

我们来考虑方面,求满足上面几个式子的,可能0个(这时候全是单纯的7的倍数),一个,两个,最多15个..这个过程我们可以用2^n二进制来巧妙枚举每次表示的是满足哪两个式子.然后根据容斥原理,奇数减偶数加.

    因为要求是7的倍数,所以也把7加入到同余式中,并一直置位1,每个集合都必须满足.

另外我们CRT解出的X是在%M的条件下的最小的X,(M为mi的乘积,就是LCM)。

那么1~y中有多少满足条件的X呢,我们知道(X+K*M)%M=X%M. 所以我们只要解方程 X+K*M<=Y就好了.

因为Y-X可能有负数,每次加个M的偏移,左右区间都加M结果无影响.

PS:

最后求出ai,Mi和x后,乘积可能会爆ll,所以这里要用到快速乘法(俄罗斯农民乘法)。

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn=1e5+10;ll n,x,y;bool vis[20]; ll a[20],m[20];void ex_gcd(ll a,ll b,ll &x,ll &y)  {      if(b==0)      {          x=1;          y=0;          return ;      }      ex_gcd(b,a%b,x,y);      int tmp=x;      x=y;      y=tmp-(a/b)*y;   }  ll solve(ll x,ll y,ll N){return (x-y)/N;} ll qmul(ll x,ll y,ll mod)//俄罗斯农民乘法 {ll res = 0;while(y){if(y&1)res = (res + x) %mod;y >>= 1;x = (x + x)%mod;}return res;}ll CRT(ll l,ll r){ll M = 1,ans = 0;for(int i = 0;i <= n;i++){if(vis[i])M *= m[i];}for(int i = 0;i <= n;i++){if(vis[i]){ll Mi = M/m[i];ll x,y;ex_gcd(Mi,m[i],x,y);ans = ((ans + qmul(x*Mi%M,a[i],M))%M+M)%M;//x可能为负数,取模要注意    }}ll res = solve(r+M,ans,M) - solve(l-1+M,ans,M);return res;}int main(){int ca = 1;int _;cin>>_;while(_--){memset(vis,0,sizeof vis);ll ans = 0;scanf("%lld %lld %lld",&n,&x,&y);m[n] = 7;a[n] = 0;vis[n] = 1;for(int i = 0;i < n;i++) scanf("%lld %lld",&m[i],&a[i]);ll all = 1 << n;for(ll i = 0;i < all ;i++){int cnt = 0;ll tmp = i;for(int j = 0;j < n ;j++){ vis[j]= tmp & 1; if(vis[j]) cnt++; tmp >>= 1;}int flag = cnt & 1 ? -1:1;ans += flag*CRT(x,y);}printf("Case #%d: %lld\n",ca++,ans);}return 0;}



原创粉丝点击