今天wxhRound考了一道不能再简单的容斥,完全做不来,感觉容斥很弱
[UVA10325] The Lottery
题意:求1~n中不能被给定m个数中任意一个数整除的数的个数
答案就是n-(被m个数中任意一个数整除的数的个数)
后面这个算法就是:
被1个数整除的数的个数-
被2个数整除的数的个数+被3个数整除的数的个数。。。。。。。
容斥实现方法:二进制压位暴力
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
ll n, m, a[20], ans;
ll gcd( ll a, ll b ){ return b == 0 ? a : gcd( b, a%b ); }
int main(){
while( scanf( "%d%d", &n, &m ) != EOF ){
for( int i = 1; i <= m; i++ ) scanf( "%lld", &a[i] );
ans = n;
for( int i = 1; i < (1<<m); i++ ){
ll ret = 1, cnt = 0;
for( int j = 1; j <= m; j++ )
if( i & (1<<(j - 1)) ){
cnt++;
ret = ret * a[j] / gcd( ret, a[j] );
if( ret > n ) break;
}
if( ret > n ) continue;
ret = n / ret;
if( cnt & 1 ) ans -= ret; else ans += ret;
}
printf( "%lld\n", ans );
}
return 0;
}
[UVA11806]
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
ll n, m, a[20], ans;
ll gcd( ll a, ll b ){ return b == 0 ? a : gcd( b, a%b ); }
int main(){
while( scanf( "%d%d", &n, &m ) != EOF ){
for( int i = 1; i <= m; i++ ) scanf( "%lld", &a[i] );
ans = n;
for( int i = 1; i < (1<<m); i++ ){
ll ret = 1, cnt = 0;
for( int j = 1; j <= m; j++ )
if( i & (1<<(j - 1)) ){
cnt++;
ret = ret * a[j] / gcd( ret, a[j] );
if( ret > n ) break;
}
if( ret > n ) continue;
ret = n / ret;
if( cnt & 1 ) ans -= ret; else ans += ret;
}
printf( "%lld\n", ans );
}
return 0;
}
Cheerleaders
题意:n*m的矩阵,放k个东西。
要求第1,n行 第1,n列必须有的方案数
答案就是所有的-少一行或一列+少两行或一列。。。
容斥实现方法:二进制压位暴力
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#include <queue>
#include <set>
#include <map>
using namespace std;
int T, n, m, C[405][405], K;
const int mod = 1000007;
int main(){
for( int i = 0; i <= 400; i++ ){
C[i][i] = C[i][0] = 1;
for( int j = 1; j < i; j++ )
C[i][j] = ( C[i - 1][j] + C[i - 1][j - 1] ) % mod;
}
scanf( "%d", &T );
for( int cas = 1; cas <= T; cas++ ){
scanf( "%d%d%d", &n, &m, &K );
int ans = 0;
for( int i = 0; i < 16; i++ ){
int r = n, c = m, cnt = 0;
if( i & 1 ){ r--; cnt++; }
if( i & 2 ){ r--; cnt++; }
if( i & 4 ){ c--; cnt++; }
if( i & 8 ){ c--; cnt++; }
if( cnt & 1 ) ans = ( ans - C[r*c][K] + mod ) % mod;
else ans = ( ans + C[r*c][K] ) % mod;
}
printf( "Case %d: %d\n", cas, ans );
}
return 0;
}
[poj3904]给出n个数
,从其中选出4个数,使它们的最大公约数为1,问总共有多少中取法。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#include <queue>
#include <set>
#include <map>
using namespace std;
int T, n, m, C[405][405], K;
const int mod = 1000007;
int main(){
for( int i = 0; i <= 400; i++ ){
C[i][i] = C[i][0] = 1;
for( int j = 1; j < i; j++ )
C[i][j] = ( C[i - 1][j] + C[i - 1][j - 1] ) % mod;
}
scanf( "%d", &T );
for( int cas = 1; cas <= T; cas++ ){
scanf( "%d%d%d", &n, &m, &K );
int ans = 0;
for( int i = 0; i < 16; i++ ){
int r = n, c = m, cnt = 0;
if( i & 1 ){ r--; cnt++; }
if( i & 2 ){ r--; cnt++; }
if( i & 4 ){ c--; cnt++; }
if( i & 8 ){ c--; cnt++; }
if( cnt & 1 ) ans = ( ans - C[r*c][K] + mod ) % mod;
else ans = ( ans + C[r*c][K] ) % mod;
}
printf( "Case %d: %d\n", cas, ans );
}
return 0;
}
答案明显就是:所有排列-1个素数倍数的数+3个素数倍数的数-2个素数倍数的数。。。。
直接考虑容斥绝对过不了
对于每一个数进行质因数分解,最多有6个质因子,可以容斥,把这个数的因数容斥出来
然后答案统计一下就好了
容斥实现方法:二进制压位暴力
ps:此题用莫比乌斯函数也行,但是没必要
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
int tot, pri[10], n, a[10005], one[10005], cnt[10005]; ll fac[10005], ans;
void init(){
ans = 0;
memset( cnt, 0, sizeof(cnt) );
memset( one, 0, sizeof(one) );
}
void solve( int x ){
memset( pri, 0, sizeof(pri) ); tot = 0;
for( int i = 2; i * i <= x; i++ )
if( x % i == 0 ){
pri[++tot] = i;
while( x % i == 0 ) x /= i;
}
if( x != 1 ) pri[++tot] = x;
for( int i = 1; i < (1<<tot); i++ ){
int k = 1, num = 0;
for( int j = 0; j < tot; j++ )
if( (1<<j) & i ){
k *= pri[j + 1];
num++;
}
cnt[k]++;
one[k] = num;
}
}
int main(){
for( int i = 4; i <= 10000; i++ ) fac[i] = 1LL * (i - 3) * (i - 2) * (i - 1) * i / 24;
while( ~scanf( "%d", &n ) ){
init();
for( int i = 1; i <= n; i++ ) scanf( "%d", &a[i] ), solve( a[i] );
for( int i = 1; i <= 2500; i++ )
if( cnt[i] ){
if( one[i] & 1 ) ans -= fac[cnt[i]];
else ans += fac[cnt[i]];
}
printf( "%lld\n", ans + fac[n] );
}
return 0;
}
spoj 4168. Square-free integers
求出1~n中不能被“完全平方数”(不含1)整除的数的个数。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
int tot, pri[10], n, a[10005], one[10005], cnt[10005]; ll fac[10005], ans;
void init(){
ans = 0;
memset( cnt, 0, sizeof(cnt) );
memset( one, 0, sizeof(one) );
}
void solve( int x ){
memset( pri, 0, sizeof(pri) ); tot = 0;
for( int i = 2; i * i <= x; i++ )
if( x % i == 0 ){
pri[++tot] = i;
while( x % i == 0 ) x /= i;
}
if( x != 1 ) pri[++tot] = x;
for( int i = 1; i < (1<<tot); i++ ){
int k = 1, num = 0;
for( int j = 0; j < tot; j++ )
if( (1<<j) & i ){
k *= pri[j + 1];
num++;
}
cnt[k]++;
one[k] = num;
}
}
int main(){
for( int i = 4; i <= 10000; i++ ) fac[i] = 1LL * (i - 3) * (i - 2) * (i - 1) * i / 24;
while( ~scanf( "%d", &n ) ){
init();
for( int i = 1; i <= n; i++ ) scanf( "%d", &a[i] ), solve( a[i] );
for( int i = 1; i <= 2500; i++ )
if( cnt[i] ){
if( one[i] & 1 ) ans -= fac[cnt[i]];
else ans += fac[cnt[i]];
}
printf( "%lld\n", ans + fac[n] );
}
return 0;
}
spoj 4168. Square-free integers
![](https://i-blog.csdnimg.cn/blog_migrate/74435ae190c99ed0188ddf3d33589238.webp?x-image-process=image/format,png)
我乱想的,搜题解结果(以上)是对的
容斥方法:莫比乌斯函数
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#include <queue>
#include <set>
#include <map>
using namespace std;
const int N = 10000000 + 5;
typedef long long ll;
ll ans, n;
int mul[N], tot, pri[1000005], T; bool isnot[N];
void mobius(){
for( int i = 2; i <= 10000000; i++ ){
if( !isnot[i] ) pri[++tot] = i, mul[i] = -1;
for( int j = 1; j <= tot && i * pri[j] <= 10000000; j++ ){
isnot[i * pri[j]] = 1;
if( i % pri[j] == 0 ) mul[i * pri[j]] = 0;
else mul[i * pri[j]] = mul[i] * -1;
}
}
}
int main(){
mobius();
scanf( "%d", &T );
while( T-- ){
scanf( "%lld", &n );
ans = n;
for( ll i = 2; i * i <= n; i++ )
ans += mul[i] * n / i / i;
printf( "%lld\n", ans );
}
return 0;
}
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <bitset>
#include <queue>
#include <set>
#include <map>
using namespace std;
const int N = 10000000 + 5;
typedef long long ll;
ll ans, n;
int mul[N], tot, pri[1000005], T; bool isnot[N];
void mobius(){
for( int i = 2; i <= 10000000; i++ ){
if( !isnot[i] ) pri[++tot] = i, mul[i] = -1;
for( int j = 1; j <= tot && i * pri[j] <= 10000000; j++ ){
isnot[i * pri[j]] = 1;
if( i % pri[j] == 0 ) mul[i * pri[j]] = 0;
else mul[i * pri[j]] = mul[i] * -1;
}
}
}
int main(){
mobius();
scanf( "%d", &T );
while( T-- ){
scanf( "%lld", &n );
ans = n;
for( ll i = 2; i * i <= n; i++ )
ans += mul[i] * n / i / i;
printf( "%lld\n", ans );
}
return 0;
}