• [省选练习]S


    [省选练习]六:状压DP

    题意

    给定一个正整数(N),数据满足(N)最多含六个不同的质因子。现在从一个空数列开始,把(N)的因数一个一个加到数列中,每次加入的数可能重复,但必须都大于(1)。每次添加新的数字的时候,需要保证这个数(x)与数列中已有的数至多一个有大于(1)的公因数。例如,当(N)等于(12156144)的时候,((42),(66,7,7,23,299,66),(42,12156144))是合法的,而数列((5,11))是不合法的,因为(5)不是(12156144)的因数,数列((66,13,143))也是不合法的,因为(143)与其他两个数有大于(1)的公因数。现在请你求出共有多少个不同的数列满足以上条件。特别地,两个数列不同,当且仅当他们的长度不同或某一处的数不同。

    分析

    大概是一道挺简单的题,不过状态设计十分新颖,用到了三进制状压。(一点都不新颖,自己这么鶸还不承认)

    (n)共有(cnt)种质因数,(sta)的前(cnt)位表示每个质因子在多少个选出数列的数中出现,容易发现这个数不会大于(2),所以可以使用三进制状压。(sta)的后(frac{cnt imes (cnt-1)}{2})位每位表示一对不同的质因数在这之后能否出现在同一个数中。(f[sta])的转移比较显然吧。

    由于C++并没有自带三进制位运算符,所以在处理三进制数时我们可以把它暂存到数组里。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cctype>
    #include <algorithm>
    #include <utility>
    #include <vector>
    #include <map>
    #define rin(i,a,b) for(int i=(a);i<=(b);i++)
    #define rec(i,a,b) for(int i=(a);i>=(b);i--)
    #define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
    typedef long long LL;
    using std::cin;
    using std::cout;
    using std::endl;
    
    inline LL read(){
    	LL x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const LL MOD=1e9+7;
    LL n;
    int cnt=-1,num[6],ano[6],forb[15],top;
    std::vector<int> fac;
    std::pair<int,int> trans[15];
    std::map<int,LL> mp;
    
    inline void getprimefactor(){
    	LL nn=n,lim=sqrt(n);
    	for(LL i=2;i<=lim;i++){
    		if(n%i) continue;
    		fac.push_back(0);cnt++;
    		while(n%i==0) fac[cnt]++,n/=i;
    	}
    	if(n!=1){
    		fac.push_back(1);cnt++;
    	}
    	std::swap(n,nn);
    }
    
    int main(){
    	n=read();
    	getprimefactor();
    	mp.clear();
    	mp[0]=1;
    	int usiz=pow(3,cnt+1)*(1<<((cnt+1)*cnt/2))-1;
    	int ii=0,jj=1;
    	rin(i,0,(cnt+1)*cnt/2-1){
    		trans[i]=std::make_pair(ii,jj);
    		jj++;if(jj>cnt) ii++,jj=ii+1;
    	}
    	LL ans=0;
    	rin(i,0,usiz){
    		if(mp.find(i)==mp.end()) continue;
    		ans+=mp[i];if(ans>=MOD) ans-=MOD;
    		int sta=(i>>((cnt+1)*cnt/2)),sat=(i&((1<<((cnt+1)*cnt/2))-1));
    		rin(j,0,cnt) num[j]=sta%3,sta/=3;
    		sta=0,top=0;
    		rin(j,0,cnt) if(num[j]<2) sta|=(1<<j);
    		rin(j,0,(cnt+1)*cnt/2-1) if((sat>>j)&1) forb[++top]=j;
    		for(int j=sta;j;j=((j-1)&sta)){
    			bool flag=0;
    			rin(k,1,top)
    				if(((j>>trans[forb[k]].first)&1)
    				&&((j>>trans[forb[k]].second)&1)){
    					flag=1;break;
    				}
    			if(flag) continue;
    			int nxtsta=j,nxtsat=0;
    			rin(k,0,cnt) ano[k]=(nxtsta&1),nxtsta>>=1;
    			nxtsta=0;int temp=1;
    			rin(k,0,cnt) nxtsta+=temp*(num[k]+ano[k]),temp*=3;
    			rin(k,0,(cnt+1)*cnt/2-1) 
    				if((num[trans[k].first]&&ano[trans[k].second])
    				||(num[trans[k].second]&&ano[trans[k].first])||((sat>>k)&1))
    					nxtsat|=(1<<k);
    			int nxt=((nxtsta<<((cnt+1)*cnt/2))|nxtsat);
    			if(mp.find(nxt)==mp.end()) mp[nxt]=0;
    			LL temp2=1;
    			rin(k,0,cnt) if(ano[k]) temp2=temp2*fac[k]%MOD;
    			mp[nxt]=(mp[nxt]+mp[i]*temp2)%MOD;
    		}
    	}
    	printf("%lld
    ",ans-1<0?ans-1+MOD:ans-1);
    	return 0;
    }
    
  • 相关阅读:
    日报 18/06/21
    collection
    日报 18/06/20
    getTickCount()函数 VS GetTickCount()函数
    临时变量不能作为非const类型引用形参的实参
    关于Char类型数据做cout输出
    静态局部变量、静态全局变量、extern全局变量、自动变量 札记
    cvKMeans2函数用法概述
    操作系统 庞丽萍 第五章
    操作系统 庞丽萍 第四章
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10180068.html
Copyright © 2020-2023  润新知