codeforces 840 C. On the Bench(多重集合的交错排列经典题目)

题目链接

C. On the Bench

分析

这是一道多重集合交错排列的经典题目,经典的题意是这样的:

设集合 A n1a1,n2a2,...,niai 问使其相邻两个数不相同的排列有多少种.

我们先来解决这个经典题目,然后再来解决codeforces的这个题目.
可以用 dp+容斥做
先来解决一个简化的问题
dp[k] 表示将这些数分成 k 块 每块的数的是一样的,并且每块数量至少为1,那么我们可以通过递推来求这个 dp 数组

dp[i][k] 表示考虑前 i 个数分成 k 块的个数,那么,

dp[i][k]=j=1kdp[i1][kj](ni1j1)ni!j!

上面那个不就是个背包吗?递推就好了

知道了 dp[k] 之后,就可以用容斥了,

dp[k]k! 表示的是有 nk 个数相邻

所以有

ans=dp[n]n!dp[n1](n1)!++(1)(ni)dp[i]i!

将原题的完全平方数处理掉就是经典问题了

AC code

#include
#define pb push_back
#define mp make_pair
#define PI acos(-1)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define ms(x,v) memset((x),(v),sizeof(x))
#define scint(x) scanf("%d",&x );
#define scf(x) scanf("%lf",&x );
#define eps 1e-10
#define dcmp(x) (fabs(x) < eps? 0:((x) <0?-1:1))
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;
const int maxn = 300+10;
const int MAX_V= 500+10;
const int MOD = 1e9+7;

LL a[maxn];
LL C[maxn][maxn];
LL A[maxn];
LL dp[maxn][maxn];
std::mapint> ma;
LL power_mod(LL x,LL n){
   LL ret =1;
   while (n) {
      if(n&1)ret = ret * x %MOD;
      x = x*x %MOD;
      n >>=1;
   }
   return ret;
}
void init(){
   C[0][0] = 1;
   for(int i=1 ; i0] =1;
      for(int j=1 ; j1][j] + C[i-1][j-1]) % MOD;
      }
   }
   A[0]=1;
   for (int i=1 ; i1]*i % MOD;
}
int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n;
    init();
    cin>>n;
    for(int i=0 ; icin>>x;
      for(int j=2 ; j*j <= x ; ++j){
         while (x % (j*j) ==0) {
            x/=(j*j);
         }
      }
      ma[x] ++;
   }
   ms(dp,0);dp[0][0] =1;
   int cnt =1;
   for(auto e : ma){
      for(int i=1 ; i<=n ; ++i){
         //dp[cnt-1][0] =1;
         int m = min(e.se,i);
         for(int j = 1 ; j<=m ; ++j){
            LL tmp = A[e.se];
            tmp  = tmp * C[e.se-1][j-1] % MOD;
            tmp = tmp *power_mod(A[j],MOD-2) % MOD;
            dp[cnt][i] += dp[cnt-1][i-j]*tmp;
            dp[cnt][i] %= MOD;
         }
      }
      cnt ++;
   }
   //for(int i=0 ; i<=n ; ++i)std::cout << dp[cnt-1][i] << '\n';
   LL ans =dp[cnt-1][n] *A[n]% MOD;int k=0;
   for(int i =n-1 ; i>=0 ; --i){
      if(k&1)ans = (ans+dp[cnt-1][i]*A[i] )% MOD;
      else{
         ans = (ans - dp[cnt -1][i] * A[i] + MOD) % MOD;
      }
      k ^=1;
   }
   std::cout << ans << '\n';

    //std::cout << "time "<< clock()/1000 <<"ms"<< '\n';
    return 0;
}

你可能感兴趣的:(算法刷题)