poj 3904 Sky Code
这个题目是不错的容斥原理的题目。
题意: 给定最多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; }
这个题目真是坑爹呀,说是容斥原理,但是我用容斥原理怎么都没想出来怎么容斥。看了别人的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; }