• P2150 [NOI2015]寿司晚宴


    https://www.luogu.com.cn/problem/P2150
    状压dp

    首先两个人选的数两两互质可以转化为没有公共的质因数,那么先把 ([2,n]) 做质因数分解,30分暴力就简单了
    直接设 (f(i,S,T)) 表示考虑前 (i) 个数,两人选的质因数集合分别时 (S,T),的方案数
    (k) 为当前数的质因数集合,转移就是 (f(i-1,S,T) ightarrow f(i,S|k,T),(k operatorname{and} T=0)) 以及 (f(i-1,S,T) ightarrow f(i,S,T|k),(k operatorname{and} S=0))

    考虑正解,因为 (22<sqrt(500)<23),所以每个数都至多有一个大于 (22) 的质因数,如果单独考虑这个大的质因数,剩下需要考虑的小质因数就只有 (8) 个了
    可以把所有数按大质因数分段,把所有大质因数相同的数分进一个段里,对于没有大质因数的数就自己一段,然后只要求出每个段的答案再合并就好了
    (f1(S,T),f2(S,T)) 分别是考虑到当前段,把这个大质数分给第一/二个人,两人所取质因数集合分别为 (S,T) 的方案数。(dp(S,T)) 则为总共的方案数
    每当进入一个新段,要把 (dp) 的值分别赋值给 (f1,f2)
    合并的话只要 (dp(S,T)=f1(S,T)+f2(S,T)-dp(S,T)) 就行,要减是因为 (f1,f2) 中会把在原来的基础上两人都不选任何数这个方案计算两编,贡献就是原来的 (dp(S,T)),减去他

    考虑 (f1,f2) 之间的转移,设 (k) 为当前数的小质因数集合,可知有 (f1(S,T) ightarrow f1(S|k,T),(koperatorname{and} T=0)) 以及 (f2(S,T) ightarrow f2(S,T|k),(koperatorname{and} S=0))
    其实和之前的暴力做法是有些相似的,只不过是在限定了大质数要给谁的基础上

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN puts("")
    inline long long read(){
    	register long long x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
    	return y?x:-x;
    }
    #define N 300
    int n;
    long long mod;
    long long f1[N][N],f2[N][N],dp[N][N];
    const int prime[8]={2,3,5,7,11,13,17,19};
    struct Node{
    	int val,S,big;
    	inline void init(){
    		for(reg int i=0;i<8;i++){
    			if(val%prime[i]) continue;
    			S|=(1<<i);
    			while(!(val%prime[i])) val/=prime[i];
    		}
    		if(val>1) big=val;
    	}
    }a[505];
    inline int cmp(Node a,Node b){return a.big<b.big;}
    int main(){
    	n=read();mod=read();
    	for(reg int i=1;i<n;i++) a[i].val=i+1,a[i].init();
    	std::sort(a+1,a+1+n,cmp);
    	dp[0][0]=1;
    	for(reg int i=1;i<n;i++){
    		if(i==1||a[i].big!=a[i-1].big||!a[i].big){
    			std::memcpy(f1,dp,sizeof f1);std::memcpy(f2,dp,sizeof f2);
    		}
    		for(reg int j=255;~j;j--)for(reg int k=255;~k;k--){//滚动数组,逆序
    			if(j&k) continue;
    			if(!(a[i].S&j)) f2[j][k|a[i].S]=(f2[j][k|a[i].S]+f2[j][k])%mod;
    			if(!(a[i].S&k)) f1[j|a[i].S][k]=(f1[j|a[i].S][k]+f1[j][k])%mod;
    		}
    		if(i==n||a[i].big!=a[i+1].big||!a[i].big){
    			//一段结束,合并答案,减掉重复计算的两人都不选的情况数
    			for(reg int j=0;j<256;j++)for(reg int k=0;k<256;k++)
    				if(!(j&k)) dp[j][k]=(f1[j][k]+f2[j][k]-dp[j][k]+mod)%mod;
    		}
    	}
    	long long ans=0;
    	for(reg int i=0;i<256;i++)for(reg int j=0;j<256;j++)
    		if(!(j&i)) ans=(ans+dp[i][j])%mod;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Day18:继续添加前端代码
    Day17:今天为前端做的几件小事
    Day16:接口调试完成&紧急转前端开发
    Day25:报告编写等
    Day24:结论的撰写&&openid再次尝试
    Day23:个人小结的撰写&&对coderunner的熟悉
    Day22:数据库导入&分工&准备科研训练
    Day21:尝试脱离相同ip连接
    Day20:update功能的实现
    Day19:代码整合等
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/14045344.html
Copyright © 2020-2023  润新知