zoukankan      html  css  js  c++  java
  • CF840C On the Bench 题解

    Codeforces
    Luogu

    Description.

    给定序列 \(\{a_i\}\),求有多少个排列是好的。
    一个排列 \(\{p_i\}\) 是好的,当且仅当

    • \(\forall i\in[2,n],a_{p_{i-1}}\times a_{p_i}\) 是完全平方数。

    Solution.

    首先肯定是容斥,求出钦定 \(k\) 对粘在一起的方案数。
    前面的容斥系数是 \((-1)^k\),原因就考虑这个枚举本质是什么。
    本质是枚举了一个集合,让它里面所有元素都不满足,所以系数就是子集反演的系数。

    然后平方数经典套路就是把平方因子去掉,接下来相同就是完全平方数。
    那考虑对所有不同的数,它们无法粘在一起,贡献可以拆开,合并是个背包。

    考虑对一种数,合并 \(j\) 个的贡献是什么,是 \(\dbinom{cnt_i-1}{j-1}\times \frac{cnt_i!}{j!}\)
    推导就考虑先 \(cnt_i!\) 排列,然后格板 \(\dbinom{cnt_i-1}{j-1}\),然后考虑这 \(j\) 种本质相同可以交换除以 \(j!\)

    Coding.

    点击查看代码
    //是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
    #include<bits/stdc++.h>
    using namespace std;typedef long long ll;
    template<typename T>inline void read(T &x)
    {
    	x=0;char c=getchar(),f=0;
    	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
    	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    	f?x=-x:x;
    }
    template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
    const int N=305,P=1e9+7;int n,a[N],cn[N],bs[N],dp[N],ut,ct[N],tn[N],tp[N];
    int fc[N],fi[N],iv[N];//dbinit{{{
    inline int ksm(int x,int q=P-2) {int r=1;for(;q;q>>=1,x=1ll*x*x%P) if(q&1) r=1ll*r*x%P;return r;}
    inline void dbinit(int n=N-1)
    {
    	fc[0]=1;for(int i=1;i<=n;i++) fc[i]=1ll*fc[i-1]*i%P;
    	iv[1]=1;for(int i=2;i<=n;i++) iv[i]=1ll*iv[P%i]*(P-P/i)%P;
    	fi[0]=1;for(int i=1;i<=n;i++) fi[i]=1ll*fi[i-1]*iv[i]%P;
    }
    inline int C(int n,int m) {return n<0||m<0||n<m?0:1ll*fc[n]*fi[m]%P*fi[n-m]%P;}//}}}
    const int V=31625;int pr[V],pc,ls[V],ph[V],mu[V];char pv[V];//prinit{{{
    inline void prinit(int n=V-1)
    {
    	pv[1]=mu[1]=ph[1]=1,ls[1]=0;for(int i=1;i<=n;i++)
    	{
    		if(!pv[i]) pr[++pc]=i,mu[i]=-1,ph[i]=i-1,ls[i]=i;
    		for(int j=1;j<=pc&&i*pr[j]<=n;j++)
    		{
    			ls[i*pr[j]]=pr[j],mu[i*pr[j]]=i%pr[j]?-mu[i]:0;
    			ph[i*pr[j]]=ph[i]*(pr[j]-!!(i%pr[j]));
    			pv[i*pr[j]]=1;if(i%pr[j]==0) break;
    		}
    	}
    }//}}}
    int main()
    {
    	prinit(),dbinit(),read(n);for(int i=1;i<=n;i++) read(a[i]);
    	for(int i=1;i<=n;i++)
    	{
    		int x=a[i];a[i]=1;for(int j=1;j<=pc&&pr[j]*pr[j]<=x;j++)
    			{int c=1;while(x%pr[j]==0) c=pr[j]^1^c,x/=pr[j];a[i]*=c;}
    		if(x^1) a[i]*=x;
    	}//钦定 k 个相邻,其他随意的方案数,相邻必须相同,相同绑在一起,背包合并
    	for(int i=1;i<=n;i++) tn[++ut]=a[i];
    	sort(tn+1,tn+ut+1),ut=unique(tn+1,tn+ut+1)-tn-1;
    	for(int i=1;i<=n;i++) cn[lower_bound(tn+1,tn+ut+1,a[i])-tn]++;
    	dp[0]=1;int lim=0;
    	for(int k=1;k<=ut;k++)
    	{
    		for(int i=0;i<cn[k];i++) bs[i]=1ll*C(cn[k]-1,i)*fc[cn[k]]%P*fi[cn[k]-i]%P;
    		//printf("%d : \n",cn[k]);for(int i=0;i<cn[k];i++) printf("%d%c",bs[i],i==cn[k]-1?'\n':' ');
    		for(int i=lim+cn[k]-1;i>=0;i--) tp[i]=0;
    		for(int i=lim;i>=0;i--) for(int j=cn[k]-1;j>=0;j--) tp[i+j]=(tp[i+j]+1ll*dp[i]*bs[j])%P;
    		lim+=cn[k]-1;for(int j=0;j<=lim;j++) dp[j]=tp[j];
    	}
    	for(int i=0;i<=lim;i++) dp[i]=1ll*dp[i]*fc[n-i]%P;
    	//for(int i=0;i<=lim;i++) printf("%d%c",dp[i],i==lim?'\n':' ');
    	int rs=0;for(int i=0;i<=lim;i++) rs=(rs+((i&1)?P-dp[i]:dp[i]))%P;
    	return printf("%d\n",rs),0;
    }
    
  • 相关阅读:
    用户行为分析之实时数据采集
    用户行为分析之离线数据采集
    RDDs之combineByKey()
    KeyValue对RDDs
    RDD基本操作之Action
    RDDs基本操作之Transformations
    Spark学习之Scala的基础知识
    Spark学习之RDDs介绍
    查看mysql中的用户和密码
    mysql-connector-java-5.-bin.jar 下载方法
  • 原文地址:https://www.cnblogs.com/pealfrog/p/15422573.html
Copyright © 2011-2022 走看看