这个题目是不错的容斥原理的题目。
题意: 给定最多1w个数,每个数不超过1w,要求从中选择4个数a,b,c,d满足gcd(a,b,c,d)==1 的个数
思路: 直接暴力的话是 时间复查度是O(10000^4*log(10000))肯定会超时
容斥原理: 从n个数中任意选择4个C(4,n) - ( 从n个中选择4个使得gcd至少为2 ) - ( 从n个中选择4个使得gcd至少为3) + ( 从n个中选择4个使得gcd至少为6) ……具体实现就是每个数,素因子分解,然后用二进制枚举其所有的因子
这个题目要注意的是,答案可能超过int,所有要用long long ,因为这还wa了一次
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=10010;
int prime[maxn],num,cnt[maxn],p[20],ok[maxn];
bool isprime[maxn];
typedef long long ll;
void init()
{
for(int i=2;i<maxn;i++)
if(!isprime[i])
{
prime[num++]=i;
ok[i]=1;
for(int j=2*i;j<maxn;j+=i)
isprime[j]=1,ok[j]++;
}
}
ll C(int a,int b)
{
ll ret=1;
for(int i=1;i<=b;i++)
ret=ret*(a-i+1)/i;
return ret;
}
int main()
{
init();
int n,tmp;
while(scanf("%d",&n)==1)
{
memset(cnt,0,sizeof(cnt));
int Max=0;
for(int i=0;i<n;i++)
{
scanf("%d",&tmp);
Max=max(Max,tmp);
int ct=0;
for(int j=0;prime[j]*prime[j]<=tmp;j++)
if(tmp%prime[j]==0)
{
p[ct++]=prime[j];
while(tmp%prime[j]==0) tmp/=prime[j];
}
if(tmp!=1) p[ct++]=tmp;
int lim=1<<ct;
for(int j=0;j<lim;j++)
{
int mul=1;
for(int r=0;r<ct;r++)
if(j&(1<<r)) mul*=p[r];
cnt[mul]++;
}
}
ll ans=0;
for(int i=1;i<=Max;i++)
if(ok[i]%2==0) ans+=C(cnt[i],4);
else ans-=C(cnt[i],4);
printf("%I64d\n",ans);
}
return 0;
}
UVA #10325 "The Lottery" [难度:简单]
这个题目之前都做过,题意是给出【 L ,R】 区间L <= R <= 2^31 ,和M个数(m<=15)求出【L,R】 区间范围类有多少个,不被这m个数任意一个数整除的数有多少个
思路: 求【1,x】 : ans = x - x / a1 - x/a2 -……- x/an + x/ lcm(a1,a2) + x/ lcm(a1,a3) + x/ lcm(a2,a3)
UVA #11806 "Cheerleaders" [难度:简单]
这个题目不知道为什么就是没有过,思路: 是C(n*m , k) - C(n*m - n ,k)*2 - C(n*m-m,k)*2 + C(n*m -n -m ,k)…… 待思考
TopCoder SRM 477 "CarelessSecretary" [难度:简单]
题意:是给出N个不同的信,然后这N个信对应不同的N个官员,恰巧指定K官员都受到错误的信,问这样情况共有几种可能?这个题目样例给的很强,我自己在纸上算了好几个样例都过了,TC上交题目很麻烦我就没有做。思路: N! - C(K,1)* (N-1)! + C(K,2)*(N-2)! - C(K,3)*(N-3)! ……
TopCoder TCHS 16 "Divisibility" [难度:简单]
跟 “The Lottery ”几乎是一样的题目
spo Another Game With Numbers 简单题目,用容斥原理求出【1,n】区间范围类不被 给定m个数都整除的个数
TopCoder SRM 382 "CharmingTicketsEasy" [难度:中等]
这个题目是挺好的题目,开始使劲的忘容斥原理方面想就是想不到怎么容斥,后来才知道其实主体思想还是应该用dp,先用dp算出满足第一个条件的个数 + dp算出满足第二个条件的个数 - 同时满足两个条件的个数
状态:f[ len ][ sum ] 表示长度为len组成各个数的和为sum的个数 dp[ len ][ sum1 ][ sum2 ] 表示长度为len 奇数为和为sum1 偶数位和为sum2 的个数,我是用的滚动数组内存优化
const int mod=999983;
long long dp[2][500][500],f[51][500];
class CharmingTicketsEasy
{
public:
int count(int K, string good)
{
int dig[10],len=good.length();
for(int i=0;i<len;i++) dig[i]=good[i]-'0';
sort(dig,dig+len);
int lim=dig[len-1]*K;
memset(f,0,sizeof(f));
memset(dp,0,sizeof(dp)) ;
f[0][0]=1;
for(int i=0;i<K;i++)
for(int j=0;j<=lim;j++)
if(f[i][j]){
for(int r=0;r<len;r++) f[i+1][j+dig[r]]=(f[i+1][j+dig[r]]+f[i][j])%mod;
}
// for(int i=0;i<=lim;i++)
// cout<<i<<" "<<f[K][i]<<endl;
dp[0][0][0]=1;
int now=0;
for(int i=0;i<K;i++)
{
for(int j=0;j<=lim;j++)
for(int r=0;r<=lim;r++)
if(dp[now][j][r])
{
if(i&1){
for(int k=0;k<len;k++)
dp[now^1][j][r+dig[k]]=(dp[now^1][j][r+dig[k]]+dp[now][j][r])%mod;
}else{
for(int k=0;k<len;k++)
dp[now^1][j+dig[k]][r]=(dp[now^1][j+dig[k]][r]+dp[now][j][r])%mod;
}
}
memset(dp[now],0,sizeof(dp[now]));
now=1-now;
}
long long ans=0;
for(int i=0;i<=lim;i++) ans=(ans+f[K][i]*f[K][i])%mod;
ans=(ans+ans)%mod;
for(int i=0;i<=lim;i++)
for(int j=0;j<=lim;j++)
if(dp[now][i][j])
{
for(int r=0;r<=lim;r++)
if(j+r>=i&&dp[now][r][j+r-i]&&i+j==j+2*r-i)
ans=((ans-dp[now][i][j]*dp[now][r][j+r-i])%mod+mod)%mod;
}
//cout<<ans<<endl;
return ans;
}
TopCoder SRM 390 "SetOfPatterns" [难度:中等]
这个题目真是坑爹呀,说是容斥原理,但是我用容斥原理怎么都没想出来怎么容斥。看了别人的dp代码,dp[ i ][ j ] 表示匹配到每个串的第i个字符 匹配的集合为j(状态压缩)的个数,先预处理,satisfy[ i ] [ j ] 表示在第i字符为 j 所能够匹配的的集合
#line 7 "SetOfPatterns.cpp"
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <fstream>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <list>
#include <stdexcept>
#include <functional>
#include <utility>
#include <ctime>
using namespace std;
#define PB push_back
#define MP make_pair
#define REP(i,n) for(i=0;i<(n);++i)
#define FOR(i,l,h) for(i=(l);i<=(h);++i)
#define FORD(i,h,l) for(i=(h);i>=(l);--i)
typedef vector<int> VI;
typedef vector<string> VS;
typedef vector<double> VD;
typedef long long LL;
typedef pair<int,int> PII;
const int mod=1000003;
int bits[1<<15],dp[2][1<<15],satisfy[50+1][26];
class SetOfPatterns
{
public:
inline void add(int &a,int b)
{
if((a+=b)>=mod) a-=mod;
}
inline int equal(char a,char b)
{
return a==b|| a=='?';
}
inline int bitCount(int m)
{
return bits[m];
}
inline int ones(int n)
{
return (1<<n)-1;
}
int howMany(vector <string> patterns, int k)
{
int n=patterns.size();
int m=patterns[0].size();
bits[0]=0;
for(int i=0;i<(1<<n);++i)
bits[i]=bits[i>>1]+ (i&1);
// cout<<bits[3]<<endl;
memset(satisfy,0,sizeof(satisfy));
for(int i=1;i<=m;i++)
for(char c='a';c<='z';c++)
for(int j=0;j<n;j++)
if(equal(patterns[j][i-1],c))
satisfy[i][c-'a']|=(1<<j);
memset(dp,0,sizeof(dp));
dp[0][ones(n)]=1;
for(int i=1;i<=m;i++)
{
memset(dp[i&1],0,sizeof(dp[i&1]));
for(int j=0;j<(1<<n);j++)
if(dp[(i+1)&1][j]>0&&bitCount(j)>=k)
for(int c=0;c<26;c++) add(dp[i&1][j&satisfy[i][c]],dp[(i+1)&1][j]);
}
int res=0;
for(int i=0;i<(1<<n);i++)
if(bitCount(i)==k) add(res,dp[m&1][i]);
return res;
}
TopCoder SRM 176 "Deranged" [难度:中等]
思路: 用二进制枚举,枚举不动点的集合,其他的数自由排列,然后如果不动点的个数为偶数则加 ,否则减去。。 注意自由排列涉及到相同的元素
LL fact[16];
int cnt[16];
class Deranged
{
public:
long long numDerangements(vector <int> nums)
{
int N=nums.size();
fact[0]=1;
for(int i=1;i<=N;i++)
fact[i]=i*fact[i-1];
LL res=0;
for(int m=0;m<(1<<N);m++)
{
int free=0;
memset(cnt,0,sizeof(cnt));
for(int i=0;i<N;i++)
if((m&(1<<i))!=0)
free++,cnt[nums[i]]++;
LL a=fact[free];
for(int i=0;i<N;i++) a/=fact[cnt[i]];
if((N-free)%2==0) res+=a;
else res-=a;
}
return res;
}