首页 > 其他分享 >状压 DP(ZR)

状压 DP(ZR)

时间:2023-01-26 23:45:58浏览次数:56  
标签:cnt 前缀 int 状压 while add ZR DP mod

[PKUSC2018]最大前缀和

从部分分出发考察性质,“满足 a 中至多一个负数”怎么做?好吧这个很简单,但是它提醒我们从负数的 POV 考虑。不难发现,最大前缀和的结束为止一定是某个负数之前,而这个位置 \(i\) 作为最大前缀和的充要条件就是 \(\forall j\in [1,i]\),前缀和 \(j\) 都 \(\le\) 前缀和 \(i\),\(forall j\in (i,n]\),前缀和 \(j\) 都 \(<\) 前缀和 \(i\)。不难发现,就是 \([1,i]\) 的每一个后缀和 \(\ge 0\),\([i+1,n]\) 的每一个前缀和 \(<0\)。

这就是分成两部分,一部分满足条件一,另一部分满足条件二的意思啊!显然的状压 DP。设 \(f(s),g(s)\) 分别表示选出并排列 \(s\),使得满足前者和后者的方案数。
最后的统计答案,发现有点小小问题,就是如果第一个数是负数,就会统计不到,所以我们令 \(f(0)=g(0)=1\),然后枚举第一个数,这样实在不行可以选空集。

#include <bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,ans,a[25],f[1<<20],g[1<<20];
inline void add(int &x,int y){(x+=y)>=mod&&(x-=mod);}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=0;i<n;i++)if(a[i+1]>=0)f[1<<i]=1;else g[1<<i]=1;
	for(int s=0;s<(1<<n);s++){
		if(s==(s&-s))continue;
		int sum=0;
		for(int i=0;i<n;i++)if(s>>i&1){
			sum+=a[i+1];
			add(f[s],f[s^(1<<i)]);
		}
		if(sum<0)f[s]=0;
	}
	for(int s=0;s<(1<<n);s++){
		if(s==(s&-s))continue;
		int sum=0;
		for(int i=0;i<n;i++)if(s>>i&1){
			sum+=a[i+1];
			add(g[s],g[s^(1<<i)]);
		}
		if(sum>=0)g[s]=0;
	}
	f[0]=g[0]=1;
	for(int i=1;i<=n;i++){
		int U=((1<<n)-1)^(1<<(i-1));
		for(int s=0;s<(1<<n);s++)if((s&U)==s){
			int sum=a[i];
			for(int j=0;j<n;j++)if(s>>j&1)sum+=a[j+1];
			sum=(sum%mod+mod)%mod;
			add(ans,1ll*f[s]*g[U^s]%mod*sum%mod);
		}
	}
	cout<<ans;
}

[PKUWC2018]随机算法

【套路】独立集 DP 时,重点观察独立集内节点的邻居集合,每加入一个点到独立集,就把它和它的邻居一起加入状态,而独立集自身通过大小刻画即可。

设 \(f(i,s)\) 表示独立集大小为 \(i\),独立集中点连同其所有邻居占据集合 \(s\),的方案数。考虑下一个 加入独立集 的点,它应该和 \(s\) 无交集,这种情况下可以转移到 f(i+1,s|(1<<k-1)|adj[k]),转移系数是 \(A_{n-1-|s|}^{|s\cup adj_k|-|s|}\)。
算出最大独立集大小 \(mx\),答案就是 \(f(mx,U)\)。

[PKUWC2018]随机游走


什么是 Min-Max 容斥?

\(\min_{i\in S} a_i=\sum_{T\subseteq S}(-1)^{\color{red}{|T|+1}}\max_{i\in T}a_i\)
\(\max_{i\in S} a_i=\sum_{T\subseteq S}(-1)^{\color{red}{|T|+1}}\min_{i\in T}a_i\)


在本题中,\(a_i\) 就是集合中各点的到达时间,最晚到达时间是所求,但最早到达时间更容易计算。
为什么最早到达时间更容易 DP 呢?这是因为如果设 \(f(i,s)\) 表示从 \(i\) 去 \(s\) 中所有点至少一次的期望步数,就不知道边界在哪,但如果设成去到 \(s\) 中最近一点的期望步数,边界就是 \(s\) 中点自身的 \(f(i)=0\)。(当然我也不确定是不是那样一定不可做,因为貌似也没错,但是容斥后做肯定可做)


考虑到根节点(起点 \(x\))不能从“父亲”转移,所以转移式没有 "fa" 一项,因此 \(f_{rt}=b_{rt}\)。Min-Max 部分用高维前缀和加速。

#include <bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,q,rt,k[20],b[20],g[1<<18];
vector<int>G[20];
inline void add(int &x,int y){(x+=y)>=mod&&(x-=mod);}
inline int qp(int a,int b){int c=1;for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)c=1ll*c*a%mod;return c;}
void dfs(int x,int p,int s){
	int sk=0,sb=0;
	for(int y:G[x])if(y^p){
		dfs(y,x,s);
		add(sk,k[y]),add(sb,b[y]);
	}
	if(s>>(x-1)&1)k[x]=b[x]=0;
	else k[x]=qp(((int)G[x].size()+mod-sk)%mod,mod-2),b[x]=1ll*(sb+G[x].size())*k[x]%mod;
}
int main(){
	cin>>n>>q>>rt;
	for(int i=1,u,v;i<n;i++)
		cin>>u>>v,G[u].emplace_back(v),G[v].emplace_back(u);
	for(int s=1;s<(1<<n);s++){
		dfs(rt,0,s);
		g[s]=b[rt]*(__builtin_popcount(s)&1?1ll:mod-1ll)%mod;
	}
	for(int w=1;w<(1<<n);w<<=1)
		for(int i=0;i<(1<<n);i+=w<<1)
			for(int j=0;j<w;j++)
				add(g[i+j+w],g[i+j]);
	while(q--){
		int K,tmp,s=0;
		cin>>K;
		while(K--)cin>>tmp,s|=1<<(tmp-1);
		cout<<g[s]<<'\n';
	}
}

[SNOI2017]遗失的答案

考虑到题意其实很简单,我们只需要对于每一个 L 的质因子,都存在一个所选数顶上界,且存在一个所选数顶下界。
考虑到 \(L\le 10^8\) 质因子数目不超过 \(8\),可以设计一个 \(2^{2\times 8}\) 的状态,表示每个质因子是否顶 L 的上界和是否顶 G 的下界。
需要一些数(须是 1~N 且是 L 的因子)的状态 or 起来是全集,不好做,想反演,or 起来是 \(S\) 的子集呢?那么如果所有满足条件的数中,状态是 \(S\) 的子集的有 \(c\) 个,答案就是 \(g(S)=2^c\)。\(f(S)\) 通过二项式反演求得。
考虑“必须选 \(x\)” 的限制,因此对于 \(x\subseteq S\) \(g(S)=\frac{2^c}2\)(有一半是选 \(x\) 的,一半不选),而 \(x\not\subseteq S\),就必定没选 \(x\),\(g(S)=0\);二项式反演部分可以通过暴力完成,因为只需要枚举 \(x\) 的超集,总共是 \(O(3^n)\) 的。

#include <bits/stdc++.h>
using namespace std;
inline int read(){
	register char ch=getchar();register int x=0;
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
const int mod=1e9+7;
int n,q,G,L,siz,h[1<<16],ppc[1<<16],_2[100005];
vector<int>P,cL,cG;
map<int,int>mp;
inline void add(int &x,int y){(x+=y)>=mod&&(x-=mod);}
inline void work(int x){
	if(x%G!=0||x>n)return;
	int st=0,o=1,i=0;
	for(int p:P){
		int cnt=0;
		while(x%p==0)x/=p,cnt++;
		st|=o*(cnt==cL[i]),st|=(o<<1)*(cnt==cG[i]);
		o<<=2,i++;
	}
	h[st]++;
}
int main(){
	n=read(),G=read(),L=read(),q=read();
	int tmp=L;
	for(int i=2;i*i<=tmp;i++)if(tmp%i==0){
		while(tmp%i==0)tmp/=i;
		P.emplace_back(i);
	}
	if(tmp>1)P.emplace_back(tmp);
	siz=P.size();
	for(int p:P){
		tmp=L;
		int cnt=0;
		while(tmp%p==0)tmp/=p,cnt++;
		cL.emplace_back(cnt);
		cnt=0,tmp=G;
		while(tmp%p==0)tmp/=p,cnt++;
		cG.emplace_back(cnt);
	}
	for(int i=1;i*i<=L;i++)if(L%i==0){
		work(i);
		if(i*i!=L)work(L/i);
	}
	siz*=2;
	for(int w=1;w<(1<<siz);w<<=1)
		for(int i=0;i<(1<<siz);i+=w<<1)
			for(int j=0;j<w;j++)
				h[i+j+w]+=h[i+j];
	for(int i=1;i<(1<<siz);i++)ppc[i]=ppc[i-(i&-i)]+1;
	_2[0]=1; for(int i=1;i<=1e5;i++)_2[i]=_2[i-1]*2%mod;
	for(int x;q--;){
		x=read();
		if(L%x!=0||x%G!=0||x>n){puts("0");continue;}
		int st=0,o=1,i=0,xx=x;
		for(int p:P){
			int cnt=0;
			while(xx%p==0)xx/=p,cnt++;
			st|=o*(cnt==cL[i]),st|=(o<<1)*(cnt==cG[i]);
			o<<=2,i++;
		}
		x=st;
		if(mp[x])cout<<mp[x]<<'\n';
		else {
			int ans=0;
			for(int t=((1<<siz)-1)^x,St=t;~t;t=(t-1)&St){
				int s=((1<<siz)-1)^t;
				if(h[s])add(ans,((siz-ppc[s])&1?mod-1ll:1ll)*_2[h[s]-1]%mod);
				if(!t)break;
			}
			cout<<(mp[x]=ans)<<'\n';
		}
	}
}

标签:cnt,前缀,int,状压,while,add,ZR,DP,mod
From: https://www.cnblogs.com/impyl/p/17068397.html

相关文章

  • 数位 DP
    概述数位DP是以从数学意义上的位数出发来DP为特点的一类DP。下述特点中的一部分可能对计数类以外的不适用。状态设计通常包含“考虑到第几位”和“是否已经......
  • P5858 「SWTR-03」Golden Sword DP+单调队列模板
    P5858「SWTR-03」GoldenSword-洛谷|计算机科学教育新生态(luogu.com.cn) 理解题意后,我们知道贪心和暴力枚举显然是不行的,联想到DP我们设置dp[i][j]表示,第i种......
  • LeetCode正则表达式匹配(lambda/dp)
    lambda表达式[捕获列表](参数列表)mutable(可选)异常属性->返回类型{//函数体}所谓捕获列表,其实可以理解为参数的一种类型,lambda表达式内部函数体在默认情况下......
  • UDP
    1.OSError:[WinError10040]一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小。接收给的内存小了当客......
  • RSA dp泄漏攻击
    dp泄漏攻击给n,e,dp,c\[\begin{array}{l}&&&&&&&&&&&&&\\dp\equivd(\bmod(p-1))\\\becausedp\cdote\equivd\cdote\equiv1(\bmod(p-1))\\\th......
  • P2657 [SCOI2009] windy 数 数位DP好题
    P2657[SCOI2009]windy数-洛谷|计算机科学教育新生态(luogu.com.cn)数位DP好题主要问题是:不含前导零且相邻两个数字之差至少为 2solution:现在枚举到了第i位......
  • 数位DP及模板
    数位DP:一般来说数位DP有两种写法:1.for循坏枚举DP2.记忆化搜索+DP这里详细将记忆化搜索+DPDFS状态:常见的dfs状态有三个:1.枚举到第几位(POS)2.判断前面是否紧贴上限(LIMI......
  • vmhost永久免费主机搭建wordpress
    vmhost主机试用+worpress搭建点击vmhost进入vmhost官网,vmhost提供了永久免费的主机,还附带一个三级域名,并且支自定义子域名,免费托管5G的网页空间,网页支持php语言,附带数据......
  • P3802 小魔女帕琪 题解【期望dp】
    题目传送门P3802解题思路本题的解题思路关键在于分段。每一个结构段的概率在之后的结构段依然适用。判断是否符合这种特性最好方法是随机截取一段观察是否成立发现成......
  • WeetCode4 —— 二叉树遍历与树型DP
    一丶二叉树的遍历1.二叉树遍历递归写法与递归序了解过二叉树的朋友,最开始肯定是从二叉树的遍历开始的,二叉树遍历的递归写法想必大家都有所了解。publicstaticvoidpro......