雅礼集训2018day4乱写

loj6501 Cube

四维超立方体根本没见过,所以考虑通过低纬度向高一维度的图形推导得到规律

考虑一个正方形得到正方体的操作,就是俗话说的面动成体

得到的正方体有贡献的部分是原正方形,结束位置的正方形,和正方形划过部分的边缘

(f[i][j])是在(i)维图形中,(j)维图形的数量

由于(i)维的图形是由(i-1)维图形移动得到的,那么开始和结束位置会有两个(i-1)维图形

移动过程中,(i-1)维图形中每个(j)维图形移动的痕迹会产生一个(j+1)维图形

所以得到

[f[i][j]=2*f[i-1][j]+f[i-1][j-1] ]

大力展开式子

[f[i][j]=4*f[i-2][j]+2*f[i-2][j-1]+2*f[i-2][j-1]+f[i-2][j-2] ]

我们发现系数其实是((2x+1)^n),答案是(x)(m)次项系数

LOJ6502 Divide

考虑如果有一种方法对(w)进行排序,使得:

对于(1le j<i),(w_i+w_jge m),或者对于(1le j<i)(w_i+w_j<m)

那么我们可以大力(dp):设(f[i][j])表示处理到第(i)个,(A)队中有(j)个元素的最大值

当情况(1)时,放入哪一队都能和另一队的所有元素搭配

(f[i][j]=max{f[i-1][j]+j,f[i-1][j-1]+i-j})

否则放入哪一队都没用

(f[i][j]=max{f[i-1][j],f[i-1][j-1]})

那么再考虑怎么让(w)排序成符合要求的亚子:

先将(w_i)升序排序

如果(w_1+w_nge m),那么把(w_n)放到当前序列最左侧,标记为(1),处理([1,n-1])

如果(w_1+w_n<m),那么把(w_n)放到当前序列最左侧,标记为(0),处理([2,n])

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
		if(ch=='-') f=0,ch=getchar();
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
		return f?x:-x;
	}
	const int N=2e3+10,p=1e9+7;
	int n,m,ret,sum;
	int a[N],w[N];
	int g[N][N],f[N][N];
	inline void main()
	{
		n=read(),m=read();
		int head=1,tail=n;
		for(int i=1;i<=n;++i) w[i]=read(); sort(w+1,w+n+1);
		for(int i=n;i>=1;--i) (w[head]+w[tail]>=m)?a[i]=1,--tail:++head;
		g[0][0]=1;
		for(int i=1;i<=n;++i)
		{
			for(int j=0;j<=i;++j)
			{
				if(j^i)
				{
					int v=f[i-1][j]+(a[i]?j:0);
					if(v==f[i][j]) (g[i][j]+=g[i-1][j])%=p;
					if(v>f[i][j]) f[i][j]=v,g[i][j]=g[i-1][j];
				}
				if(j)
				{
					int v=f[i-1][j-1]+(a[i]?i-j:0);
					if(v==f[i][j]) (g[i][j]+=g[i-1][j-1])%=p;
					if(v>f[i][j]) f[i][j]=v,g[i][j]=g[i-1][j-1];
				}
				ret=max(ret,f[i][j]);
			}
		}
		for(int i=0;i<=n;++i) sum=(sum+(f[n][i]==ret?g[n][i]:0))%p;
		printf("%lld %lld
",ret,sum);
	}
}
signed main()
{
	red::main();
	return 0;
}
/*
10 10
1 5 6 2 6 3 4 9 5 6

*/

LOJ6503Magic

我们先假设每张卡牌有标号,最后再除以(prodlimits_{i=1}^{m}a_i)

这东西看上去不能直接算,考虑容斥,设(f_i)是恰好有(i)对的情况,(g_i)是至少有(i)对的情况

那么显然

[g_k=sumlimits_{i=k}^{n} binom{i}{k}f_i ]

二项式反演得到

[f_k=sumlimits_{i=k}^{n}(-1)^{i-k} binom{i}{k}g_i ]

考虑求(g(x))

对于第(i)种卡片,我们强制至少有(x)对魔术对,问题等价于将(a_i)张卡片分成(a_i-x)个排列的方案数

考虑先算出全排列(a_i!),然后将整个数列用隔板法分组( binom{a_i-1}{a_i-x-1}),但是排列之间是没有顺序的,而隔板法分出来是有顺序的,所以除以((a_i-x)!)结果是

[ binom{a_i-1}{a_i-x-1}*frac{a_i!}{(a_i-x)!} ]

所以

[h_i(x)=sumlimits_{j=1}^{a_i}frac{a_i!* binom{a_i-1}{j-1}}{j!}x^j ]

此时(h_i(x))是分成(x)个排列的方案数

所有(h(x))乘起来得到(g(x)),同时确定最后排序之间的相对位置,答案乘上(j!)

[g(x)=sumlimits_{j=1}^{n}([x^j]prodlimits_{i=1}^{m}h_i(x))*j!*x^j ]

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
		if(ch=='-') f=0,ch=getchar();
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
		return f?x:-x;
	}
	const int N=1e5+10,p=998244353;
	int n,m,k,ret;
	int a[N],sum[N];
	int fac[N],inv[N];
	int A[N<<2],B[N<<2];
	vector<int> st[N],g;
	int pos[N<<2];
	inline int fast(int x,int k)
	{
		int ret=1;
		while(k)
		{
			if(k&1) ret=ret*x%p;
			x=x*x%p;
			k>>=1;
		}
		return ret;
	}
	inline int C(int n,int m)
	{
		if(n<m) return 0;
		return fac[n]*inv[m]%p*inv[n-m]%p;
	}
	inline void ntt(int limit,int *a,int inv)
	{
		for(int i=0;i<limit;++i)
			if(i<pos[i]) swap(a[i],a[pos[i]]);
		for(int mid=1;mid<limit;mid<<=1)
		{
			int Wn=fast(3,(p-1)/(mid<<1));
			for(int r=mid<<1,j=0;j<limit;j+=r)
			{
				int w=1;
				for(int k=0;k<mid;++k,w=w*Wn%p)
				{
					int x=a[j+k],y=w*a[j+k+mid]%p;
					a[j+k]=x+y;
					if(a[j+k]>=p) a[j+k]-=p;
					a[j+k+mid]=x-y;
					if(a[j+k+mid]<0) a[j+k+mid]+=p;
				}
			}
		}
		if(inv) return;
		inv=fast(limit,p-2);reverse(a+1,a+limit);
		for(int i=0;i<limit;++i) a[i]=a[i]*inv%p;
	}
	inline int check(int tl,int tr)
	{
		int l=tl,r=tr,ret=tl;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			if(sum[mid]-sum[tl-1]<=sum[tr]-sum[mid-1]) ret=mid,l=mid+1;
			else r=mid-1;
		}
		return ret;
	}
	inline vector<int> solve(int l,int r)
	{
		if(l==r) return st[l];
		int mid=check(l,r),tot=sum[r]-sum[l-1];
		vector<int> ansl=solve(l,mid),ansr=solve(mid+1,r),ans;
		ans.clear();
		int limit=1,len=0;
		while(limit<=tot) limit<<=1,++len;
		for(int i=0;i<limit;++i) pos[i]=(pos[i>>1]>>1)|((i&1)<<(len-1));
		for(int i=0;i<(int)ansl.size();++i) A[i]=ansl[i];
		for(int i=ansl.size();i<limit;++i) A[i]=0;
		for(int i=0;i<(int)ansr.size();++i) B[i]=ansr[i];
		for(int i=ansr.size();i<limit;++i) B[i]=0;
		ntt(limit,A,1),ntt(limit,B,1);
		for(int i=0;i<limit;++i) A[i]=A[i]*B[i]%p;
		ntt(limit,A,0);
		for(int i=0;i<=tot;++i) ans.push_back(A[i]);
		return ans;
	}
	inline void main()
	{
		m=read(),n=read(),k=read();
		for(int i=fac[0]=1;i<=n;++i) fac[i]=fac[i-1]*i%p;
		inv[n]=fast(fac[n],p-2);
		for(int i=n-1;~i;--i) inv[i]=inv[i+1]*(i+1)%p;
		for(int i=1;i<=m;++i) a[i]=read();
		sort(a+1,a+m+1);
		for(int i=1;i<=m;++i)
		{
			st[i].push_back(0);
			for(int j=1;j<=a[i];++j)
			{
				st[i].push_back(C(a[i]-1,j-1)*fac[a[i]]%p*inv[j]%p);
			}
		}
		for(int i=1;i<=m;++i) sum[i]=sum[i-1]+a[i];
		g=solve(1,m);	
		for(int i=0;i<=n;++i) g[i]=g[i]*fac[i]%p;
		for(int i=0;i<=n-k;++i)
		{
			if((k-(n-i))&1) (ret-=C(n-i,k)*g[i])%=p;
			else (ret+=C(n-i,k)*g[i])%=p;
		}
		ret=(ret+p)%p;
		for(int i=1;i<=m;++i) ret=ret*inv[a[i]]%p;
		printf("%lld
",ret);
	}
}
signed main()
{
	red::main();
	return 0;
}

原文地址:https://www.cnblogs.com/knife-rose/p/12886113.html