[Kuangbin带你飞]专题十四 数论(三)未A待补

临近JSCPC,特此把以前做过的数论题撸出来整理板子

C - Aladdin and the Flying Carpet

题意:告诉矩形的面积a,以及边长不得小于b,问有几种方案

不知道代码为啥wa了

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
using namespace std;
const int N=1e6+100;
int prime[N];
int tag[N];
int cnt=0;
typedef long long LL;
void getp()
{
    memset(tag,0,sizeof(tag));
    tag[0]=1;
    tag[1]=1;
    for(int i=2;i<N;++i)
    {
        if(!tag[i])
            prime[++cnt]=i;
        for(int j=1;j<=cnt&&prime[j]*i<N;++j)
        {
            tag[prime[j]*i]=1;
            if(i%prime[j]==0)
                break;
        }
    }
//    for(int i=1;i<=10;++i)
//        cout<<prime[i]<<' ';
//    cout<<endl;
}
long long calc(long long  x)//计算x的因子个数 为(1+alpha1)*(1+alpha2)...即素因子的幂次
{
    long long ans=1;
    for(int i=1;i<=cnt&&prime[i]*prime[i]<=x;i++)
    {
        if(x%prime[i]==0)
        {
            int m=0;
            while(x%prime[i]==0)
            {
                x/=prime[i];
                m++;
            }
            //cout<<prime[i]<<' '<<m<<' ';
            ans*=(1+m);
           // cout<<ans<<endl;
        }
    }
    if(x>1) ans*=2;     //说明x是素数
    ans/=2;
    //printf("%lld\n",ans);
}
int main()
{
    getp();
    int T;
    scanf("%d",&T);
    for(int i=1;i<=T;++i)
    {
        long long a,b;
        scanf("%lld%lld",&a,&b);
        LL ans=0;
        if(b*b>a)                   //边的下界的平方大于面积 无解
        {
            ans=0;
            printf("Case %d: %lld\n",i,ans);
        }
        else
        {   ans=calc(a);
            for(LL j=1;j<b;++j)     //小于b的答案无效
               if(a%j==0)
               ans--;               //前面已经除过2了 且b*b<=a 不用担心多减
            printf("Case %d: %lld\n",i,ans);
        }
    }
    return 0;

}

H-pairs Form LCM

题意:为n以内有多少数对的LCM为n

这个博主写的很好,待补题orz

L - Fantasy of a Summation  快速指数幂

给出下面代码要求优化。

思路:res一共执行了n^k次,那么总共加了k*n^k的个数,可知每个数被加的次数是一样的,所以每个数都被加了k*n^(k-1)次。所以最终答案就是所有数的和*k*n^(k-1)

#include <stdio.h>

int cases, caseno;
int n, K, MOD;
int A[1001];

int main() {
    scanf("%d", &cases);
    while( cases-- ) {
        scanf("%d %d %d", &n, &K, &MOD);

        int i, i1, i2, i3, ... , iK;

        for( i = 0; i < n; i++ ) scanf("%d", &A[i]);

        int res = 0;
        for( i1 = 0; i1 < n; i1++ ) {
            for( i2 = 0; i2 < n; i2++ ) {
                for( i3 = 0; i3 < n; i3++ ) {
                    ...
                    for( iK = 0; iK < n; iK++ ) {
                        res = ( res + A[i1] + A[i2] + ... + A[iK] ) % MOD;
                    }
                    ...
                }
            }
        }
        printf("Case %d: %d\n", ++caseno, res);
    }
    return 0;
}
//A
#include<iostream>
#include<stdio.h>
using namespace std;
typedef long long LL;
LL qmod(LL a,LL b,LL mod)
{
    if(b==0) return 1;
    LL ans=1;
   // cout<<"a: "<<a<<endl;
    while(b>0)
    {
        if(b&1) ans=ans*a%mod;
       // cout<<"ans: "<<ans<<endl;
        a=a*a%mod;
        b=b>>1;
    }
    return ans;
}
int main()
{
    int T;
    cin>>T;
    int kase=0;
    while(T--)
    {
        ++kase;
        LL n,k,mod;
        cin>>n>>k>>mod;
        LL sum=0;
        for(int i=1;i<=n;++i)
        {
            LL x;
            cin>>x;
            sum=(sum+x)%mod;
        }
        //cout<<sum<<endl;
        LL ans=qmod(n,k-1,mod);
        //cout<<ans<<endl;
        ans=ans*k%mod*sum%mod;
        printf("Case %d: %lld\n",kase,ans);
    }
    return 0;
}

P - Code Feat

记得很久以前按照别人的思路写的T了

中国剩余定理和扩展中国剩余定理讲解 比较清晰,但是代码不是很符合我的习惯

题意:有一个正整数N满足C个条件,每个条件都形如“它除以X的余数在集合{Y1,Y2...Yk}中”,所有条件中的X两两互素,你的任务是找出最小的S个解。按照从小到大排序。

这是我T了的代码。

//T
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<string.h>
#include<stdio.h>
using namespace std;
typedef long long LL;
const int N=15;
const int M=105;
int x[N],c,s;
int k[N];
int y[N][M];
LL a[N];
LL Mul=1;
int now;
set<int>value[N];
vector<LL> ans; //存一种组合得到的ans,通过它加M的整数倍获得其他答案,ans[i]是在i组搭配下的最小答案了,不应该用vector 应该用set的

LL extgcd(LL a,LL b,LL &X,LL &Y)  //O(lgn)
{
    if(!b) {X=1;Y=0;return a;}
    LL d=extgcd(b,a%b,Y,X);
    Y-=a/b*X;
    return d;
}

LL CRT()
{

    LL ans=0;
    /*Mul=1;
    for(int i=0;i<c;++i)
        Mul*=x[i];*/
    // cout<<Mul<<endl;     一开始bug在M不应该在这里计算,还是重复计算了
    for(int i=0;i<c;++i)    //事实上这里也不需要重复计算四次的,再开个位置存mi关于x[i]的逆就好
    {
        LL mi=Mul/x[i];
        LL X,Y;
        extgcd(mi,x[i],X,Y);
        ans=(ans+a[i]*X*mi)%Mul;
    }
    return (ans+Mul)%Mul;
}
void dfs(int layer)    //分配a[N]
{
    if(layer==c)
    {
        /*for(int i=0;i<c;++i)
            cout<<a[i]<<endl;*/   //a[i]的分配是正确的
        ans.push_back(CRT());  //已经获得一组A
        return ;
    }
    for(int i=0;i<k[layer];++i)
    {
        a[layer]=y[layer][i];
        dfs(layer+1);
    }
}
void solve_CRT()
{
    ans.clear();
    dfs(0);             //生成所有的组合并获得所有的CRT值
    sort(ans.begin(),ans.end());
//    for(int j=0;j<ans.size();++j)
//        cout<<ans[j]<<endl;
    for(int i=0;;++i)
        for(int j=0;j<ans.size();++j)
    {
        LL n=Mul*i+ans[j];   //ans[j]是小于M的所以这样排一遍一定是最小的
        //cout<<n<<endl;
        if(n>0) {--s; cout<<n<<endl; if(s==0) return ;}
    }
}
void solve_enum()
{
    for(int i=0;i<c;++i)
    {
        if(i==now) continue; //不在选择的那一个k/x下选答案
        value[i].clear();
        for(int j=0;j<k[i];++j)
            value[i].insert(y[i][j]);
    }
    for(int t=0;;++t)   //枚举x*t+y(t=1,2,3......)
    {
        for(int i=0;i<k[now];++i)       //枚举y的值
        {
            LL n=x[now]*t+y[now][i];
            if(n==0) continue;         //去换下一个y
            bool ok=true;
            for(int j=0;j<c;++j)
            {
                if(j==now) continue;
                if(!value[j].count(n%x[j])) {ok=false;break;} //去换下一个i
            }
            if(ok)                      //所有的x都能被n整除
            {
               --s;
               cout<<n<<endl;
               if(s==0)
                return ;
            }
        }
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(cin>>c>>s&&c&&s)
    {
         now=0;
        for(int i=0;i<c;++i)
        {
            cin>>x[i]>>k[i];
            Mul*=x[i];
            if(k[now]*x[i]>k[i]*x[now])  //选一个k/x尽量小的
                now=i;
            for(int j=0;j<k[i];++j)
                cin>>y[i][j];
            sort(y[i],y[i]+k[i]);       //可能的余数从小到大排序
        }
        if(Mul>10000) solve_enum();
        else solve_CRT();
        cout<<endl;
    }
    return 0;
}

Q - Emoogle Grid

题意:给你M*N的格子,其中B个格子被封锁了。要将所有未被封锁的格子上色,每个格子可以上k种颜色,但是竖直方向相邻的两个格子不能是相同的颜色。现在是知道有多少种涂色方案,要求求出M

这是我RE的代码,心酸

#include<iostream>
#include<set>
#include<algorithm>
#include<math.h>
#include<map>
#include<stdio.h>
using namespace std;
typedef long long LL;
const LL mod=1e8+7;
int n,k,b,r;
int m,first_row;
const int N=510;
int x[N],y[N];
set<pair<int,int> > s;
LL mul_mod(LL a,LL b)
{
    return a%mod*b%mod;
}
LL pow_mod(LL a,LL b)
{
    LL ans=1;
    while(b)
    {
        if(b&1) ans=ans%mod*a%mod;
        a=a%mod*a%mod;
        b>>=1;
    }
    return ans;
}
LL extgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0) {x=1;y=0;return a;}
    LL d=extgcd(b,a%b,y,x);
    y-=a/b*x;
}
LL inv(LL a)
{
    LL x,y;
    LL d=extgcd(a,mod,x,y);
    if(d!=1) return -1;
    else return (x+mod)%mod;
}
LL log_mod(LL a,LL b)
{
    int m=ceil(sqrt(mod));
    LL v=inv(pow_mod(a,m));
    map<LL,LL> x;
    x[1]=0;     //a^0==1
    for(int i=1;i<m;++i)
    {
        LL e=mul_mod(e,a);
        if(!x.count(e)) x[e]=i;
    }
    for(int i=0;i<m;++i)
    {
        if(x.count(b))
            return i*m+b;
        b=mul_mod(b,v);
    }
    return -1;
}
int solve()
{
    int c=0;//前m行可凃k种颜色格子
    for(int i=0;i<b;++i)
    {
        if(x[i]!=m&&!s.count(make_pair(x[i]+1,y[i]))) ++c;
    }
    c+=n-first_row;
    LL cnt=mul_mod(pow_mod(k,c),pow_mod(k-1,(LL)m*n-b-c));
    if(cnt==r) return m;
    c=0;
    for(int i=0;i<b;++i)
        if(x[i]==m) ++c;
    cnt=mul_mod(cnt,pow_mod(k-1,n-c));
    if(cnt==r) return m+1;
    int p=pow_mod(k-1,n);
    int v=inv(cnt);
    return log_mod(p,mul_mod(r,v))+m+1;
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int kase=1;kase<=T;++kase)
    {
        scanf("%d%d%d%d",&n,&k,&b,&r);
        s.clear();
        m=1;
        first_row=0;
        for(int i=0;i<b;++i)
        {
            scanf("%d%d",&x[i],y[i]);
            s.insert(make_pair(x[i],y[i]));
            if(x[i]>m) m=x[i];
            if(x[i]==1) ++first_row;
        }
        printf("%d\n",solve());
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值