题解——再数17
T2的位置,并不算难,并且是对容斥原理的很好的应用。
题目描述
**给你一个有 n个正整数的列An。一个正整数 x 若满足在数列 An 中存在一个正整数 ai,使 x≡17(mod Ai),那么 x 就是一个‘ cost数’。 请问 1到 m的正整数 中, 有多少‘cost?’数?
小 co 看得一头雾水,只好又来请教你 **
输入
第一行两个 正整数 n和 m,意义见问题描述 。
第二行 n个正整数
输出
1个整数 , 表示 cost 的个数 。
数据范围
n <= 30 , Ai > 17 , m 和 Ai 保证在 int 范围内
题解
考虑容斥原理,我们要找出重复的数量。
提一下,容斥原理是可以从 3 扩展的。
说简单点,就是奇加偶减
开始引用原题解:(此人太懒了)
这时我们需要用个dfs枚举每个ai选与不选,若选出k个数,且k为奇数,则ans加上m/LCM(选出的数),若为偶数ans减去m/LCM(选出的数)。当然,还要加上优化,包括可行性剪枝(选出的数的LCM要小于等于m。由于ai >17,该剪枝十分有效)、优化搜索顺序(先搜大数)。时间复杂度:O(2nlogm)(实际小得多)。
AC代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll N , M , A[ 62 ] , ans ;
inline ll read()
{
ll s = 0,w = 1;
char g = getchar();
while(g<'0'||g>'9'){if(g=='-')w*=-1;g = getchar();}
while(g>='0'&&g<='9'){s = s*10+g-'0';g = getchar();}
return s*w;
}
void prepare_(){
freopen("c17.in","r",stdin);
freopen("c17.out","w",stdout);
M = read() , N = read() - 17 ;
for( register int i = 1 ; i <= M ; i++ )A[ i ] = read() ;
sort( A + 1 , A + M + 1 ) ;
}
ll gcd( ll a , ll b ){
return ( b == 0 )?a:gcd( b , a%b ) ;
}
void dfs( ll now , ll h , ll s ){ // h shows + -
if( s >= N || now > M ) return ;
ll t = s / gcd( s , A[ now ] ) * A[ now ] ;
if ( h % 2 ) ans -= N / t ;
else ans += N / t ;
dfs( now + 1 , h + 1 , t ) ;
dfs( now + 1 , h , s ) ;
}
int main(){
prepare_();
if( N < 0 ){ printf("0");return 0 ;}
if( N == 1 ){ printf("1");return 0 ;}
else dfs( 1 , 0 , 1 ) ;
cout << ans + 1 ;
return 0;
}