• 雅礼集训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;
    }
    
    
  • 相关阅读:
    1.8新特性
    线程池
    微服务简介
    缓存三大问题
    Redis分布式锁的正确实现方式
    【java-10&11&12】java语言(Hello World相关)
    【postman】postman 安装失败
    【java-04-09集】JDK的下载和安装&配置环境变量(临时和永久)&命令行方式
    【ISTQB】TM&TA&TTA区别
    【git】学习地址
  • 原文地址:https://www.cnblogs.com/knife-rose/p/12886113.html
Copyright © 2020-2023  润新知