容斥定理学习:http://www.cppblog.com/vici/archive/2011/09/05/155103.aspx
hdu 1796 How many integers can you find
http://acm.hdu.edu.cn/showproblem.php?pid=1796
题意:给定n和一个大小为m的集合,集合元素为非负整数。求1...n - 1内能被集合里任意一个数整除的数字个数。n<=2^31,m<=10
思路:
首先明白对于集合[1,n]内能被a整除的数的个数为n/a,既能被a整除又能被b整除的数的个数为n/lcm(a,b)(a,b的最小公倍数);
容斥原理地简单应用。先找出1...n - 1内能被集合M中任意一个元素整除的个数,再减去能被集合中任意两个整除的个数,即能被它们俩的最小公倍数整除的个数,因为这部分被计算了两次,然后又加上三个时候的个数,然后又减去四个时候的倍数...直接枚举状态0...(1<<m),然后判断状态内涵几个集合元素,然后计算lcm和能被整除的个数,最后判断下集合元素的个数为奇还是偶,奇加偶减。这里回溯搜索M元素的组合也行:
这用到的是关于集合的容斥公式:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 50004 #define N 5007 #define M 200007 using namespace std; int n,m; int a[11]; int GCD(int x,int y) { if (y == 0) return x; else return GCD(y,x%y); } int LCM(int x,int y) { return (x/GCD(x,y))*y; } void solve(int mm) { int i,j; int state = (1<<mm); int ans = 0; for (i = 1; i < state; ++i)//枚举所有状态m集合的组合 { int bits = 0,lcm = 1; for (j = 0; j < mm; ++j)//查看含有那几个状态 { if (i & (1<<j)) { bits++; lcm = LCM(lcm,a[j]);//计算最小公倍数 } } if (bits & 1) ans += (n - 1)/lcm;//求个数 else ans -= (n - 1)/lcm; } printf("%d\n",ans); } int main() { //freopen("din.txt","r",stdin); int i,x; while (~scanf("%d%d",&n,&m)) { int mm = 0; for (i = 0; i < m; ++i) { scanf("%d",&x); if (x != 0) a[mm++] = x;//把0排除 } solve(mm); } return 0; }
hdu 4336 Card Collector
http://acm.hdu.edu.cn/showproblem.php?pid=4336
题意:
给出n种卡片在零食中出现的概率p[i],求如果集齐这n种卡片平均要买多少袋零食。零食里面最多只存在一张卡片。
思路:
看着像是关于概率的容斥公式其实不是,这里我们计算出存在卡片的组合要买多少张卡片才能得到,例如 0.2 0.1 买5张才能得到第一张卡片,买10张才能得到第二张卡片,如果记得到第一张又得到第二张的话要买1/(0.2 + 0.1)张卡片,这里就会存在重复性计算,所以用容斥定理排除 。其实就是一个关于集合的容斥定理公式。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 50004 #define N 5007 #define M 200007 using namespace std; double p[22]; int n; int main() { int i,j; while (~scanf("%d",&n)) { for (i = 0; i < n; ++i) scanf("%lf",&p[i]); int state = (1<<n); double ans = 0.0; for (i = 1; i < state; ++i)//枚举所有状态 { double tmp = 0.0; int bits = 0; for (j = 0; j < n; ++j) { if (i & (1<<j)) { bits++; tmp += p[j]; } } if (bits & 1) ans += 1.0/tmp;//1/tmp表示的是要得i状态就要买这些张卡片 else ans -= 1.0/tmp; } printf("%.6lf\n",ans); } return 0; }
hdu 1695 GCD
http://acm.hdu.edu.cn/showproblem.php?pid=1695
题意:
给定x属于[a,b],y属于[c,d]求GCD(x,y) = k的对数,这里(x=5, y=7) and (x=7, y=5) 视为一对,a = c = 1.
0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000,
思路:
暴力做的话是O(n^2*log)级别的肯定超时,这里首先要明白上边给的材料里面讲的一点就是求a与[1,n]里面互素的个数可以转化成求a与[1,n]不互素的个数然m后用n - m即可。那么a与[1,n]不互素的个数怎么求呢?首先求出a的所包含的有质因子,将设他的质因子为A,B,C那么AUBUC表示能与a不互质的个数,首先求出分别与A,B,C能够整除的个数,然后减去同时能够整除A*B的个数,一次类推....就转化成了容斥定理集合的应用了。
这里我们求的GCD(x,y) = k 我们可以转化成 GCD(x/k,y/k) = 1;将设区间b < b则[1,b]与[1,b]形成的满足条件的对数就是phi[1] +phi[2] + .....+ phi[b]了。对于[1,b]与[b + 1,d]我们就采用容斥定理求求不互素的个数,然后总数减去它后得到互素个数,然后总数加上即可。
状态压缩实现:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100004 #define N 5007 #define M 200007 using namespace std; ll euler[maxn]; int prime[maxn][20],num[maxn]; int n; int GCD(int x,int y) { if (y == 0) return x; else return GCD(y,x%y); } int LCM(int x,int y) { return x/GCD(x,y)*y; } void Euler()//欧拉筛选 { int i,j; CL(num,0); for (i = 1; i < maxn; ++i) euler[i] = i; for (i = 2; i < maxn; ++i) { if (euler[i] == i) { for (j = i; j < maxn; j += i) { euler[j] = euler[j]/i*(i - 1); prime[j][num[j]++] = i;//数j有num[j]个素数他们是i } } euler[i] += euler[i - 1];//累加 //printf(">>%d\n",i); } } int main() { //freopen("din.txt","r",stdin); int i,j,t,cas = 1; int a,b,c,d,k; Euler(); scanf("%d",&t); while (t--) { scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); if (k == 0) { printf("Case %d: 0\n",cas++); continue; } b /= k; d /= k; ll ans = 0; if (b > d) swap(b,d); ans += euler[b]; //test; //printf("%d %d %d\n",b,d,ans); for (i = b + 1; i <= d; ++i)//枚举[b +1,d]区间 { int tmp = 0; for (j = 1; j < (1<<num[i]); ++j)//枚举i包含素数的所有状态 { //printf(">>%d %d\n",j,num[i]); int bits = 0,lcm = 1; for (k = 0; k < num[i]; ++k) { if (j & (1<<k)) { bits++; lcm = LCM(lcm,prime[i][k]); } } if (bits & 1) tmp += b/lcm; else tmp -= b/lcm; } ans += (b - tmp); } printf("Case %d: %I64d\n",cas++,ans); } return 0; }
DFS实现:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100004 #define N 5007 #define M 200007 using namespace std; ll euler[maxn]; int prime[maxn][20],num[maxn]; int n; int GCD(int x,int y) { if (y == 0) return x; else return GCD(y,x%y); } int LCM(int x,int y) { return x/GCD(x,y)*y; } void Euler() { int i,j; CL(num,0); for (i = 1; i < maxn; ++i) euler[i] = i; for (i = 2; i < maxn; ++i) { if (euler[i] == i) { for (j = i; j < maxn; j += i) { euler[j] = euler[j]/i*(i - 1); prime[j][num[j]++] = i; } } euler[i] += euler[i - 1]; //printf(">>%d\n",i); } } ll tmp; void dfs(int x,int count,int pos,int b,int lcm) { lcm = LCM(lcm,prime[pos][x]); if (count & 1) tmp += b/lcm; else tmp -= b/lcm; for (int i = x + 1; i < num[pos]; ++i) { dfs(i,count + 1,pos,b,lcm); } } int main() { //freopen("din.txt","r",stdin); int i,j,t,cas = 1; int a,b,c,d,k; Euler(); scanf("%d",&t); while (t--) { scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); if (k == 0) { printf("Case %d: 0\n",cas++); continue; } b /= k; d /= k; ll ans = 0; if (b > d) swap(b,d); ans += euler[b]; for (i = b + 1; i <= d; ++i) { tmp = 0; for (j = 0; j < num[i]; ++j)//对素数的组合进行DFS求解 dfs(j,1,i,b,1); ans += (b - tmp); } printf("Case %d: %I64d\n",cas++,ans); } return 0; }