首页 > 代码库 > NOI2015 寿司晚宴

NOI2015 寿司晚宴

题目大意

给出$2$到$n$共$n-1,n\le 500$个数字,求从中选出两个集合使得从两个集合内各取任意一个数字互质的方案数。

简要题解

要满足题中的条件,其实就是要求两个集合中出现的质因子不同。注意到$n,n\le 500$以内的数字,要么只存在一个大于$\sqrt{n}$的质因子,要么可以由前$8$个质数分解。对于后者,拉出来用$8$位$bit$表示质因子情况压位DP出$f[i][j][k]$表示从前$i$个数字中取出两个集合内质因子状态分别为$j,k$的方案数;再考虑把前者加到DP中去,对于前者中的数,只需要确定把它放在哪个集合即可,而不需要去记录在状态中,因为我们一次性考虑完所有带这个大于$\sqrt{n}$的质因子的数:要么不选,要么只能放到同一个集合中去,这样就可以减少状态同时避免了后效性。

想的时候确实考虑到所有的合数可以由前$8$个素数筛完,但没有进一步想到如何利用这个性质来简化状态。

然后就是喜闻乐见地打错下标WA了一发。。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 namespace my_header {
  4 #define pb push_back
  5 #define mp make_pair
  6 #define pir pair<int, int>
  7 #define vec vector<int>
  8 #define pc putchar
  9 #define clr(t) memset(t, 0, sizeof t)
 10 #define pse(t, v) memset(t, v, sizeof t)
 11 #define bl puts("")
 12 #define wn(x) wr(x), bl
 13 #define ws(x) wr(x), pc(‘ ‘)
 14     const int INF = 0x3f3f3f3f;
 15     typedef long long LL;
 16     typedef double DB;
 17     inline char gchar() {
 18         char ret = getchar();
 19         for(; (ret == \n || ret == \r || ret ==  ) && ret != EOF; ret = getchar());
 20         return ret; }
 21     template<class T> inline void fr(T &ret, char c =  , int flg = 1) {
 22         for(c = getchar(); (c < 0 || 9 < c) && c != -; c = getchar());
 23         if (c == -) { flg = -1; c = getchar(); }
 24         for(ret = 0; 0 <= c && c <= 9; c = getchar())
 25             ret = ret * 10 + c - 0;
 26         ret = ret * flg; }
 27     inline int fr() { int t; fr(t); return t; }
 28     template<class T> inline void fr(T&a, T&b) { fr(a), fr(b); }
 29     template<class T> inline void fr(T&a, T&b, T&c) { fr(a), fr(b), fr(c); }
 30     template<class T> inline char wr(T a, int b = 10, bool p = 1) {
 31         return a < 0 ? pc(-), wr(-a, b, 0) : (a == 0 ? (p ? pc(0) : p) : 
 32             (wr(a/b, b, 0), pc(0 + a % b)));
 33     }
 34     template<class T> inline void wt(T a) { wn(a); }
 35     template<class T> inline void wt(T a, T b) { ws(a), wn(b); }
 36     template<class T> inline void wt(T a, T b, T c) { ws(a), ws(b), wn(c); }
 37     template<class T> inline void wt(T a, T b, T c, T d) { ws(a), ws(b), ws(c), wn(d); }
 38     template<class T> inline T gcd(T a, T b) {
 39         return b == 0 ? a : gcd(b, a % b); }
 40     template<class T> inline T fpw(T b, T i, T _m, T r = 1) {
 41         for(; i; i >>= 1, b = b * b % _m)
 42             if(i & 1) r = r * b % _m;
 43         return r; }
 44 };
 45 using namespace my_header;
 46 
 47 const int MAXN = 500 + 10;
 48 const int PNUM = 8;
 49 const int SNUM = 1 << 8;
 50 const int PRI[] = {2, 3, 5, 7, 11, 13, 17, 19};
 51 int n, mod;
 52 int fac[MAXN], rem[MAXN];
 53 int f[2][2][MAXN][MAXN], d[2][MAXN][MAXN], (*g)[MAXN];
 54 
 55 void add(int &x, int y) {
 56     x += y;
 57     if (x >= mod)
 58         x -= mod;
 59 }
 60 
 61 int main() {
 62 #ifdef lol
 63     freopen("129.in", "r", stdin);
 64     freopen("129.out", "w", stdout);
 65 #endif
 66 
 67     fr(n, mod);
 68     for (int i = 2; i <= n; ++i) {
 69         rem[i] = i;
 70         for (int j = 0; j < PNUM; ++j)
 71             if (rem[i] % PRI[j] == 0) {
 72                 fac[i] |= 1 << j;
 73                 while (rem[i] % PRI[j] == 0)
 74                     rem[i] /= PRI[j];
 75             }
 76     }
 77     int cur = 0, lst = 1;
 78     d[cur][0][0] = 1;
 79     for (int i = 2; i <= n; ++i)
 80         if (rem[i] == 1) {
 81             swap(cur, lst);
 82             memcpy(d[cur], d[lst], sizeof d[cur]);
 83             for (int j = 0; j < SNUM; ++j)
 84                 for (int k = 0; k < SNUM; ++k) {
 85                     if (!(k & fac[i]))
 86                         add(d[cur][j | fac[i]][k], d[lst][j][k]);
 87                     if (!(j & fac[i]))
 88                         add(d[cur][j][k | fac[i]], d[lst][j][k]);
 89                 }
 90         }
 91     g = d[cur];
 92 
 93     //for (int i = 0; i < 4; ++i) {
 94     //    for (int j = 0; j < 4; ++j)
 95     //        ws(g[i][j]);
 96     //    bl;
 97     //}
 98 
 99     for (int i = 2; i <= n; ++i)
100         if (rem[i] != 1) {
101             cur = 0, lst = 1;
102             memcpy(f[0][cur], g, sizeof f[0][cur]);
103             memcpy(f[1][cur], g, sizeof f[1][cur]);
104             for (int y = rem[i], x = y; x <= n; x += y) {
105                 rem[x] = 1;
106                 swap(cur, lst);
107                 memset(f[0][cur], 0, sizeof f[0][cur]);
108                 memset(f[1][cur], 0, sizeof f[1][cur]);
109                 for (int j = 0; j < SNUM; ++j)
110                     for (int k = 0; k < SNUM; ++k) {
111                         add(f[0][cur][j][k], f[0][lst][j][k]);
112                         add(f[1][cur][j][k], f[1][lst][j][k]);
113                         if (!(k & fac[x]))
114                             add(f[0][cur][j | fac[x]][k], f[0][lst][j][k]);
115                         if (!(j & fac[x]))
116                             add(f[1][cur][j][k | fac[x]], f[1][lst][j][k]);
117                     }
118             }
119             for (int j = 0; j < SNUM; ++j)
120                 for (int k = 0; k < SNUM; ++k)
121                     g[j][k] = ((f[0][cur][j][k] + f[1][cur][j][k] - g[j][k]) % mod + mod) % mod;
122         }
123     int ans = 0;
124     for (int j = 0; j < SNUM; ++j)
125         for (int k = 0; k < SNUM; ++k)
126             add(ans, g[j][k]);
127     wt(ans);
128 
129     return 0;
130 }

 

NOI2015 寿司晚宴