3.29省选模拟赛 除法与取模 dp+组合计数

LINK:除法与取模

鬼题。不过50分很好写。考虑不带除法的时候 其实是一个dp的组合计数。

考虑带除法的时候需要状压一下除法操作。

因为除法操作是不受x的大小影响的 所以要状压这个除法操作。

直接采用二进制状压是不明智的 2的个数最多为13个 2^13也同样到达了1e4的复杂度。

考虑 hash状压 即 2的个数有x个 那么我们就有状态w表示2还有x个。

这样做的原因是把一些相同的东西给合并起来 而并非分散开来。即有多个2直接记录有多少个即可。

可以发现 这样做不同的除数最多只有5个 状态量较少。

如何hash 考虑采用P进制版本的hash 这样寻找下一个决策什么的也很方便。

如:对于第i个数 进制为(当前个数+1) 这样不需要取模我们的状态量也只有不到100.

可以发现这样的hash不重不漏 缺点是 我们需要知道某个状态代表的是什么意思 去掉某个值后 得到的状态会是什么。

这样做x状态数状态转移+x状态x当前的值.

前者复杂度极低 后者 考虑第一步做的时候 x会除以2所以一半都不需要枚举了 且后面的那个x当前的值也会降低一半 一下就变成了50005000状态的dp了。

第二部做的时候还会除以2 可以发现无效的状态很多我们对于无效的状态不枚举转移即可。

(题解上说是有一个1/16的常数 所以可以通过此题。

dp的时候转移比较ex 值得注意的是 %一个数字或者除以一个数字的时候 如果有重复的存在必须要处理一下相对的关系。

前者 要乘以h[w]//w出现了多少次 因为取模过后剩余的数字被安排过了。那些数字之间不存在先后 因为是排列过的。

后者 发现此时的选择也同样有g[j][w]种 要乘上这个东西的方案。

hash的时候要注意 某个数的0的个数时的状态表示。dp的时候注意两层循环都要倒序枚举。

一道非常优异的dp题目。

const int MAXN=200010,maxn=10010;
int a[MAXN],b[MAXN],q[MAXN],c[MAXN],w[MAXN],p[MAXN],vis[MAXN];
ll fac[MAXN<<1],inv[MAXN],in[MAXN],f[maxn][110];
int g[MAXN][10],v[MAXN],h[MAXN];
int n,m,maxx,s,top,sum,tot;
inline ll ksm(ll b,int p)
{
	ll cnt=1;
	while(p)
	{
		if(p&1)cnt=cnt*b%mod;
		b=b*b%mod;p=p>>1;
	}
	return cnt;
}
inline void prepare()
{
	fac[0]=1;in[1]=1;
	rep(1,maxx,i)
	{
		fac[i]=fac[i-1]*i%mod;
		if(i!=1)in[i]=(mod-mod/i)*in[mod%i]%mod;
	}
	inv[maxx]=ksm(fac[maxx],mod-2);
	fep(maxx-1,0,i)inv[i]=inv[i+1]*(i+1)%mod;
}
inline void get_state()
{
	int ww=0;
	rep(1,sum,i)ww=ww*(w[i]+1)+q[i];
	tot=max(tot,ww);
	rep(1,sum,i)g[ww][i]=q[i],v[ww]+=q[i];
}
inline void dfs(int x)
{
	if(x==sum+1)
	{
		get_state();
		return;
	}
	rep(0,w[x],i)
	{
		q[x]=i;
		dfs(x+1);
	}
}
int main()
{
	freopen("1.in","r",stdin);
	get(s);get(n);get(m);maxx=n+m;
	prepare();
	rep(1,n,i)
	{
		get(a[i]);
		if(a[i]>s){putl(fac[n+m]);return 0;}
	}
	ll ww=1;
	rep(1,n,i)
	{
		if(a[i]>1)q[++top]=a[i];
		ww=ww*a[i];
		if(ww>s){putl(fac[n+m]);return 0;}
	}
	sort(q+1,q+1+top);
	rep(1,top,i)
	{
		if(q[i]==q[i-1])++w[sum],++i;
		if(i<=top)c[++sum]=q[i],++w[sum];
	}
	p[sum]=1;
	fep(sum-1,1,i)p[i]=p[i+1]*(w[i]+1);
	dfs(1);//整理状态
	rep(1,m,i)get(b[i]),++vis[min(b[i],s+1)],++h[b[i]];
	rep(1,s+1,i)vis[i]+=vis[i-1];
	f[s][tot]=fac[n+m]*inv[n+m-(vis[s+1]-vis[s])-(n-top)]%mod;
	fep(s,0,i)
	{
		//if(i==0)cout<<"www"<<endl;
		fep(tot,0,j)
		{
			if(!f[i][j])continue;
			//枚举要取模的数字.
			rep(1,i,k)
			{
				if(!h[k])continue;
				//当前还有数字为 v[j]+vis[i]-1
				//剩余有效数字为 v[j]+vis[i%k]
				f[i%k][j]=(f[i%k][j]+h[k]*f[i][j]%mod*fac[v[j]+vis[i]-1]%mod*inv[v[j]+vis[i%k]]%mod)%mod;
			}
			rep(1,sum,k)//枚举要除以的数字.
			{
				if(g[j][k])
				{
					//当前还有数字为 v[j]-1+vis[i]
					//剩余有效数字为 v[j]-1+vis[i/c[k]]
					f[i/c[k]][j-p[k]]=(f[i/c[k]][j-p[k]]+g[j][k]*f[i][j]%mod*fac[v[j]-1+vis[i]]%mod*inv[v[j]-1+vis[i/c[k]]]%mod)%mod;
				}
			}
		}
	}
	/*fep(s,0,i)
	{
		cout<<i<<‘ ‘<<"www"<<‘ ‘;
		fep(tot,0,j)
		cout<<f[i][j]<<‘ ‘;
		cout<<endl;
	}*/
	putl(f[0][0]);
	return 0;
}

原文地址:https://www.cnblogs.com/chdy/p/12612164.html

时间: 2025-02-01 11:54:29

3.29省选模拟赛 除法与取模 dp+组合计数的相关文章

2018/3/29 省选模拟赛 80

我真是太菜了... T1 10分纯暴力没写,20分容斥也没写(人就是懒死的).还有30分矩乘不会 正解 <IOI2018 中国国家集训队第一阶段作业题解部分 - 杭州第二中学 吴瑾昭.pdf>最后一题 T2  以为自己能拿到50分,但是其实那个暴力算法只能过10分的点,n=2000的20分数据用n^3带剪枝过不了,所以拿了30分. 正解 <计数与期望问题选讲 CLJ.pdf>最后一题 我记得我以前好像看到过这个文档但是没读过..今天读一下 T3 依然是分情况50分,30分树归20分

2018.6.29 省选模拟赛

*注意:这套题目应版权方要求,不得公示题面. 从这里开始 Problem A 小D与电梯 Problem B 小D与田野 Problem C 小D与函数 Problem A 小D与电梯 题目大意 假设电梯在0层,容量无限大.给定$n$个人的到达时间和要去的楼层.电梯上下一层楼花费的时间为1,电梯开关门.乘客上下的耗时不计,电梯可以停留在某一层楼.问将所有人送达到目的地,并让电梯回到0层的最少耗时. 先按到达时间将所有人排序.显然,每次电梯运输的人是一段连续的区间中的人. 用$f[i]$表示将前$

3.29省选模拟赛 矩形

LINK:矩形 一个大小为n的01方阵 m次询问 每次询问求出大小为a行b列的合法矩形的个数. 一个矩阵合法当且仅当其边缘都是为1. \(n,m\leq 1500\) 2s,256mb 考虑暴力 预处理出 r[i][j],d[i][j] 分别表示向右向下延伸的最长长度. 求答案的时候 枚举每个点 判断一下即可. 考试的时候写了一个一维的st表 和 单调队列做了这个判断. 两种方法时间复杂度一样 n^2m. 考虑优化 从第一种方法中我们考虑采用bitset来优化. b[i][j][k]表示长度>=

Divide two numbers,两数相除求商,不能用乘法,除法,取模运算

问题描述:求商,不能用乘法,除法,取模运算. 算法思路:不能用除法,那只能用减法,但是用减法,超时.可以用位移运算,每次除数左移,相当于2倍. 1 public class DividTwoIntegers { 2 public int divide(int dividend, int divisor) 3 { 4 if(divisor == 0) return Integer.MAX_VALUE; 5 if(divisor == -1 && dividend == Integer.MIN

通过逆元实现大数据除法的取模

当题目中数据较大,而且计算中出现过除法的时候.往往取模会出错 当计算 (A/B) % c    等价于  (A*B1)% c 其中 B1 是 B 的逆元. 那么逆元如何求呢. 先给出逆元的定义 a*x ≡1 (mod n)  ,如果x是方程的解,则x称作 a 关于模 n 的逆. a的逆元存在是有条件的: 方程ax-ny==1 要有解 则 1必须是gcd(a,n)的倍数 ,因此,a和n必须素质, 即 gcd(a,n)==1 在这个前提下 ax≡1(mod n) 只有唯一解. 现在我们来证明上面的结

多项式 - 除法与取模

一类问题:给定一个 \(n\) 次多项式 \(F(x)\) 和一个 \(m\) 次多项式 \(G(x)\),请求出多项式 \(Q(x)\),\(R(x)\),满足以下条件: \(Q(x)\) 次数为 \(n?m\),\(R(x)\) 次数小于 \(m\) \(F(x)=Q(x)?G(x)+R(x)\) 所有的运算在模 \(998244353\) 意义下进行. 多项式求逆 放一篇博客.直接做多项式的除法是无从下手的,或者说没有很优秀的方法可以优化它的复杂度. 然而逆元是个很方便的东西,乘法和除法是

@省选模拟赛03/16 - T3@ 超级树

目录 @description@ @solution@ @accepted code@ @details@ @description@ 一棵 k-超级树(k-SuperTree) 可按如下方法得到:取一棵深度为 k 的满二叉树,对每个节点向它的所有祖先连边(如果这条边不存在的话). 例如,下面是一个 4-超级树: 请统计一棵 k-超级树 中有多少条不同的简单有向路径,对 mod 取模. input 一行两整数 k, mod. output 一行一整数表示答案. example input1: 2

4.3 省选模拟赛 石子游戏 树上博弈

注意观察题目 每个点都只能将石子给自己的两个儿子 且石子个数>=1. 显然 这是一个阶梯NIM. 只有和最后一层的奇偶性相同的层才会有贡献 证明也很显然. 那么这其实就是近乎NIM游戏了 胜负自然取决于所有有贡献的石子堆的异或和. 但是 上午我傻了的一点 没有分清SG函数和NIM游戏的联系. 在NIM游戏中SG函数其实就是每个有贡献的石子堆的石子数. 再来看这道题 由于异或和一定 暴力枚举移动哪一堆石子 判断是否可行即可. 这个操作其实是 NIM游戏的证明问题了.解决的方案是 观察一下移动后造成

「模拟赛20191019」B 容斥原理+DP计数

题目描述 将\(n\times n\)的网格黑白染色,使得不存在任意一行.任意一列.任意一条大对角线的所有格子同色,求方案数对\(998244353\)取模的结果. 输入 一行一个整数\(n\). 输出 一行一个整数表示答案对\(998244353\)取模的值. 样例 样例输入 3 样例输出 32 数据范围 对于\(100\%\)的数据,\(1\leq n\leq 300\). 比第一题难了不知道多少-- 这种东西怎么看都是容斥嘛. 我们先考虑对角线没有限制的情况: 枚举行和列有多少个是同色的,