一波水题(两个区间互质对数,选x个不同数的方案数,记忆化搜索)

Coprime Integers

题意: x ∈ [ l 1 , r 1 ] , y ∈ [ l 2 , r 2 ] x\in[l1,r1],y\in[l2,r2] x[l1,r1],y[l2,r2],求gcd(x,y)=1的对数

解析:

莫比乌斯函数,定义g(x)表示gcd=x的对数,定义 f ( x ) = ∑ g ( i x ) f(x)=\sum g(ix) f(x)=g(ix),那么 g ( x ) = ∑ μ ( i ) f ( i x ) g(x)=\sum \mu(i)f(ix) g(x)=μ(i)f(ix) f ( x ) f(x) f(x)就表示 g c d = i x gcd=ix gcd=ix的方案数,直接算即可。

#include<bits/stdc++.h>
using namespace std;
#define D long long
#define F double
#define mmm(a,b) memset(a,b,sizeof(a))
D read(){ D ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
const int N=10000000;
const F pi=acos(-1);

D n,m,_n,_m,p=1e9+7,mi;
D f[N+9],mu[N+9];
D pri[664600],now;
bool vis[N+9];
void init(){
    mmm(vis,0);mi=min(m,n);now=0;
    for(D i=1;i<=mi;i++){
        f[i]=((n/i)-(_n-1)/i)*((m/i)-(_m-1)/i);
    }
    mu[1]=1;
    for(int i=2;i<=N;i++){
        if(!vis[i]){
            pri[++now]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=now&&i*pri[j]<=N;j++){
            vis[i*pri[j]]=true;
            if(i%pri[j])mu[i*pri[j]]=-mu[i];//出现新的素数 mu=-mu
            else{
                mu[i*pri[j]]=0;             //出现相同素数
                break;
            }
        }
    }
}

int main(){
    _n=read(),n=read(),_m=read(),m=read();
    init();
    D ans=0;
    for(int i=1;i<=mi;i++)
        ans+=f[i]*mu[i];

    printf("%lld\n",ans);
}

Contest Setting

题意: 有n个数,选出x个不同的数的方案数

解析:

先统计每一种数出现的次数,用dp处理到第i种数,已经选j种的方案数。 d p [ i ] [ j ] = ( d p [ i ] [ j ] + d p [ i − 1 ] [ j − 1 ] ∗ a [ i ] + d p [ i − 1 ] [ j ] ) dp[i][j]=(dp[i][j]+dp[i-1][j-1]*a[i]+dp[i-1][j]) dp[i][j]=(dp[i][j]+dp[i1][j1]a[i]+dp[i1][j])

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(register int i=a;i<=b;i++)
#define repp(i,a,b) for(register int i=a;i>=b;i--)
#define mmm(p) memset(p,0,sizeof p)
#define pill pair<int,int>
#define debug(i) printf("#%d\n",i)
#define D long long
typedef long long LL;
int read(){ int ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}

map<LL,LL>vis;
LL a[1009],ct;
LL dp[1009][1009];
int main(){
    int n=read(),k=read();
    rep(i,1,n){
        LL tmp=read();
        vis[tmp]++;
    }
    for(map<LL,LL>::iterator it=vis.begin();it!=vis.end();it++){
        a[++ct]=(*it).second;
    }
    rep(i,0,1001)dp[i][0]=1;
    rep(i,1,ct){
        rep(j,1,k){
            dp[i][j]=(dp[i][j]+dp[i-1][j-1]*a[i]+dp[i-1][j])%mod;
        }
    }
    printf("%lld\n",dp[ct][k]);
}

Count The Bits

题意: n和k,问 0 到 2 k − 1 0到2^{k}-1 02k1中的n的倍数在二进制下的1的个数和。

解析:

以为是个数学题。。。从第k位往下搜,每一位填1或者0。维护一个模n的余数和已有的1的个数,到第一位余数为0说明可以整除。记忆化搜索加速。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(register int i=a;i<=b;i++)
#define repp(i,a,b) for(register int i=a;i>=b;i--)
#define mmm(p) memset(p,0,sizeof p)
#define pill pair<int,int>
#define debug(i) printf("#%d\n",i)
#define D long long
#define F double
typedef long long LL;
LL read(){ LL ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}


const int mod= 1000000009;
int n,k;
int vis[131][1009][131];

int dfs(int pos,int have,int ct){
    if(pos==1){
        if(have==0)return vis[pos][have][ct]=ct;
        return vis[pos][have][ct]=0;
    }
    int ans=0;
    int to=have*2%n;
    if(vis[pos-1][to][ct]>=0){
        ans+=vis[pos-1][to][ct];
    }
    else {
        ans+=dfs(pos-1,to,ct);
    }
    to=(have*2+1)%n;
    if(vis[pos-1][to][ct+1]>=0){
        ans=(ans+vis[pos-1][to][ct+1])%mod;
    }
    else{
        ans=(ans+dfs(pos-1,to,ct+1))%mod;
    }
    return vis[pos][have][ct]=ans;
}

int main(){
    memset(vis,-1,sizeof(vis));
    n=read(),k=read();
    int ans=dfs(k+1,0,0);
    printf("%d\n",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值