「NOI2015」寿司晚宴

题意

给定 \(n−1\) 种不同的寿司,第 \(i\) 种寿司的美味度为 \(i+1\),小 G 和小 W 从中挑选一些来品尝,要求他们选得寿司中美味度必须都互质,问有多少种方案满足小 G 和小 W 的要求。

答案对 \(p\) 取模。

\(2\leq n \leq 500\)\(0 \le p \leq 10^9\)

题解

一道很妙的题。

首先我们看 30 分怎么做,当 \(n \leq 30\) 时只有 \(10\) 个质数,那么我们可以状压两个人选择的质因子集合。

那么记 \(dp_{i,j,k}\) 表示做到第 \(i\) 个寿司,第一个人选择的集合为 \(j\),第二个人选择的集合为 \(k\) 的方案数。

对于一个寿司,如果它的质因子都不在第一个人的集合里,那么就可以放在第二个人的集合里。

那么转移就是 \(dp_{i,j,k}+=dp_{i,j|fac_i,k}\) \(if(k \& fac_i==0)\)\(dp_{i,j,k}+=dp_{i,j,k|fac_i}\) \(if(j \& fac_i==0)\),其中 \(fac_i\) 表示第 \(i\) 个寿司的集合。

100 分做法就是我们考虑能不能减小状压的集合大小。

那么我们可以想到一个东西:对于 \(n\) ,它最多只有一个质因子大于 \(\sqrt{n}\)

那么我们把因子分成大于 \(\sqrt{n}\) 和小于 \(\sqrt{n}\) 两类。

所有大因子相同的数只能放在一个集合中,所以按照大因子的大小排序,把所有大因子相同的数放在一起算。

在计算相同的大因子的数时,我们可以把 \(f[i][j]\) 拆成 \(g[0][i][j]\)\(g[1][i][j]\) 分别表示这段数都放在第一个人的集合和第二个人的集合里的方案数。

那么合并答案就是 \(f[i][j]=g[0][i][j]+g[1][i][j]-f[i][j]\)

然后就过了。

不懂我为啥交 luogu 上 T 一个点,在 loj 上能全过。

#include <bits/stdc++.h>
#define reg register
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define SZ(x) ((int) (x).size())
#define debug(typ...) fprintf(stderr, typ)
using namespace std;
const int iINF=numeric_limits<int>::max();
const ll lINF=numeric_limits<ll>::max();
int fastin() {
  reg int x=0,ch=getchar(),f=0;
  while(!isdigit(ch)) (ch=='-')&&(f=1),ch=getchar();
  while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
  return f?-x:x;
}
const int MAXN=10;
int n,p;
vector<pair<int,int> > fac;
ll f[1<<MAXN][1<<MAXN],g[2][1<<MAXN][1<<MAXN];
int pw[101];
const int pr[9]={ 2,3,5,7,11,13,17,19 };
void init() {
  pw[0]=1;
  for(reg int i=1;i<=30;++i) pw[i]=pw[i-1]<<1;
  for(reg int i=2;i<=n;++i) {
    int tmp=i,temp=0;
    for(reg int j=0;j<=7;++j) {
      if(tmp%pr[j]) continue;
      temp|=pw[j];
      while(tmp%pr[j]==0) tmp/=pr[j];
    }
    fac.emplace_back(mp(tmp,temp));
  }
  sort(fac.begin(),fac.end());
}
ll add(reg ll a,reg ll b) {
  return a+b>=p?a+b-p:a+b;
}
ll dec(reg ll a,reg ll b) {
  return a-b<0?a-b+p:a-b;
}
void work() {
  n=fastin(),p=fastin();
  init(),f[0][0]=1;
  for(reg int i=0;i<SZ(fac);++i) {
    if(fac[i].fi==1||fac[i].fi!=fac[i-1].fi||i==1) memcpy(g[0],f,sizeof(g[0])),memcpy(g[1],f,sizeof(g[1]));
    for(reg int j=(1<<8)-1;j>=0;j--) {
      for(reg int k=(1<<8)-1;k>=0;k--) {
        if((j&fac[i].se)==0) g[1][j][k|fac[i].se]=add(g[1][j][k|fac[i].se],g[1][j][k]);
        if((k&fac[i].se)==0) g[0][j|fac[i].se][k]=add(g[0][j|fac[i].se][k],g[0][j][k]);
      }
    }
    if(fac[i].fi==1||fac[i].fi!=fac[i+1].fi||i==SZ(fac)-1) {
      for(reg int j=(1<<8)-1;j>=0;j--) {
        for(reg int k=(1<<8)-1;k>=0;k--) f[j][k]=add(g[0][j][k],dec(g[1][j][k],f[j][k]));
      }
    }
  }
  ll ans=0;
  for(reg int i=(1<<8)-1;i>=0;i--) for(reg int j=(1<<8)-1;j>=0;j--) ans=add(ans,f[i][j]);
  printf("%lld\n",ans);
}
signed main() {
  int _=1;
  // _=fastin();
  while(_--) work();
  return 0;
}
posted @ 2020-11-12 22:54  Nylch  阅读(92)  评论(0编辑  收藏  举报