URAL 1091 Tmutarakan Exams (DP或容斥)
2016-05-03 20:40
330 查看
题意
给出一个K和S,求从小于S的数里取出一个K元组的gcd大于1的K元组的数量。思路
可以dp做,dp的话是经典的计数问题,而且因为数据比较小写起来也比较简单,dp[i][j][k]表示枚举到i时取了j个数此时的gcd为k的个数,则dp[i+1][j+1][gcd(k,i+1)]+=dp[i][j][k],dp[i+1][j][k]+=dp[i][j][k]。或者容斥,因为所有合数都能用素数来表示,所以我们先枚举所有素数,然后对各个相同的或者不同的素数和进行容斥,总之就是枚举出来所有素数的组合然后奇数加偶数减就可以了。
DP代码
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; #define LL long long #define Lowbit(x) ((x)&(-x)) #define lson l, mid, rt << 1 #define rson mid + 1, r, rt << 1|1 #define MP(a, b) make_pair(a, b) const int INF = 0x3f3f3f3f; const int maxn = 1e5 + 7; const double eps = 1e-8; const double PI = acos(-1.0); LL dp[55][55][55]; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int K, S; while (scanf("%d%d", &K, &S) != EOF) { memset(dp, 0, sizeof(dp)); for (int i = 0; i <= S; i++) dp[i][1][i] = 1; for (int i = 1; i < S; i++) for (int j = 1; j <= K; j++) for (int k = 1; k <= S; k++) { dp[i+1][j][k] += dp[i][j][k]; dp[i+1][j+1][__gcd(k, i+1)] += dp[i][j][k]; } int ans = 0; for (int i = 2; i <= S && ans <= 10000; i++) ans += dp[S][K][i]; if (ans > 10000) ans = 10000; printf("%d\n", ans); } return 0; }
容斥代码
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; #define LL long long #define Lowbit(x) ((x)&(-x)) #define lson l, mid, rt << 1 #define rson mid + 1, r, rt << 1|1 #define MP(a, b) make_pair(a, b) const int INF = 0x3f3f3f3f; const int maxn = 1e5 + 7; const double eps = 1e-8; const double PI = acos(-1.0); vector<int> prime; vector<int> cnt; //某个素数的可取倍数的数量 int vis[55]; void init() { memset(vis, 0, sizeof(vis)); for (int i = 2; i <= 50; i++) { if (!vis[i]) prime.push_back(i); for (int j = i + i; j <= 50; j += i) vis[j] = 1; } } LL C(int n, int m) { if (m > n - m) m = n - m; LL res = 1; for (int i = 0; i < m; i++) { res *= n - i; res /= i + 1; } return res; } LL ans; int K, S; void dfs(int cur, int cnt, int now) { if (now * K > S) return ; if (cur >= prime.size()) { if (!cnt) return ; if (cnt & 1) ans += C(S / now, K); else ans -= C(S / now, K); return ; } dfs(cur + 1, cnt + 1, now * prime[cur]); dfs(cur + 1, cnt, now); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); init(); while (scanf("%d%d", &K, &S) != EOF) { ans = 0; dfs(0, 0, 1); printf("%lld\n", min(ans, 10000LL)); } return 0; }
相关文章推荐
- N
- Linear regression and Normal Equations algorithm
- 共同学习Java源代码--常用工具类--StringBuffer(一)
- Lucene 搜索功能
- JAVA中引用本身占用内存空间的问题
- Java连接mysql中遇到的一些问题及解决方法
- 【连载】关系型数据库是如何工作的?(1) - 前言
- Android学习记录(十) 弹框阻塞处理
- C经典 定义一维数组与二维数组
- Android(1) elipse离线安装adt后,没有图编显示
- css3滤镜-色彩(Chroma)滤镜
- java集合框架
- bat命令中的变量声明及使用
- Android-Using DDMS
- Mybatis开发Dao
- J
- iptables
- 多线程多任务断点下载
- HDU 1166 敌兵布阵 树状数组
- 数据结构实验报告2