CSP模拟<反思> 1

7.20

打了一天模拟赛,垫底了。

考场上想到了期望dp,拿到了30,但其实 \(op1\)\(op2\) 还可拿20
一看题解 原根 懵逼,但可用倍增优化dp,将 \(m\) 二进制拆分
为什么呢?
假如一个长为 \(l (为偶数)\)的串相乘且,则可以分为两个相同长度的串,总串的余数为两个串的余数相乘取模。
这里采用边倍增边更新

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=1e5+10;
int mgml(int x,int p,int mm){
	int ans=1;
	while(p){
		if(p&1) ans=(ans*x)%mm;
		x=(x*x)%mm;
		p>>=1;
	}
	return ans;
}
int f[5][1005];
int g[5][1005];
int sum[N];
int n,m,mo;
signed main(){
    cin>>n>>m>>mo;
    int tmp=m;
    int z;
	for(int i=1;i<=n;i++){
    	int x;
    	scanf("%lld",&x);z=x;
    	g[0][x]++;
	}
	int of=0,og=0;
	f[of][1]=1;
	while(m){
		if(m&1){
			memset(f[of^1],0,sizeof(f[of^1]));
			for(int j=1;j<=mo;j++){
				for(int k=1;k<=mo;k++){
					int s=(k*j)%mo;
			    	f[of^1][s]+=(f[of][j]*g[og][k]%mod);
			    	f[of^1][s]%=mod;	
				}
			}
			of^=1;
		}
		memset(g[og^1],0,sizeof(g[og^1]));
		for(int j=1;j<=mo;j++){
			for(int k=1;k<=mo;k++){
				int s=(k*j)%mo;
				g[og^1][s]+=(g[og][j]*g[og][k]%mod);
				g[og^1][s]%=mod;
			}
		}
		og^=1;
		m>>=1;
	}
//	for(int i=0;i<m;i++){//原方程
//		memset(f[op^1],0,sizeof(f[op^1]));
//		for(int j=1;j<mo;j++){//枚举 余数 
//			if(f[op][j]){
//			    for(int k=1;k<mo;k++){
//			    	if(sum[k]==0) continue;
//			    	int s=(k*j)%mo;
//			    	f[op^1][s]+=f[op][j]*sum[k];
//			    	f[op^1][s]%=mod;
//				} 
//			}
//		}
//		op^=1;
//	}
	int sum=0;
	for(int i=1;i<mo;i++){
		sum+=f[of][i]*i;
		sum%=mod;
	}
	int fm=mgml(n,tmp,mod);
	cout<<sum*mgml(fm,mod-2,mod)%mod;
}
/*
2 2 3
1 2
*/

这道是整场考试最可做的了,考场打的高斯消元和暴力,结果写挂了。
对于 \(b\) 数组,考虑换根dp(其实是模板)设 \(size\) 表示当前节点子树的 \(a\) 之和,然后求出根节点 \(b\) ,从根节点往下推就行了。式子为
\(b[y]=b[x]+size[1]-2*size[y]\)

对于 \(a\) 数组,还是一样的,将上面的式子转化的到 $$size[y]=(b[x]-b[y])/2+size[1]/2$$ $$a[y]=size[y]-\sum_{i为y的儿子节点}{size[i]}$$

\[b[1]=\sum_{1<=i<=n}{a[i]*len[i]} \]

可以求解\(size[1]\) 进而求解 \(a[i]\).

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int head[N*2],nex[N*2],ver[N*2],tot=0;
int size[N];
int f[N];
int a[N],b[N];
int d[N]; 
int sum[N],k[N];
int summ[N],kk[N];
void add(int x,int y){
	ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
	ver[++tot]=x,nex[tot]=head[y],head[y]=tot;
}
void dfs1(int x,int d,int fa){
	size[x]=a[x];
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==fa) continue;
		dfs1(y,d+1,x);
		size[x]+=size[y];
		f[x]+=f[y]+size[y];
	}
}
void dfs2(int x,int fa){
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==fa) continue;
		f[y]=f[x]-size[y]*2+size[1];
		dfs2(y,x);
	}
}
int sum_,k_;
void dfs_1(int x,int fa){
	d[x]=d[fa]+1;
	sum[x]=1;//size[1]的个数 
	k[x]=b[fa]-b[x];//常数 
	summ[x]=1;
	kk[x]=b[fa]-b[x];
	if(x==1){
		sum[x]=2;
		k[x]=0;
	}
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==fa) continue;
		dfs_1(y,x);
		sum[x]-=summ[y];
		k[x]-=kk[y];
	}
	sum_+=sum[x]*(d[x]-1);
	k_+=k[x]*(d[x]-1);
}
void clear(){
	tot=0;
	sum_=0,k_=0;
	memset(head,0,sizeof(head));
	memset(ver,0,sizeof(ver));
	memset(nex,0,sizeof(nex));
	memset(f,0,sizeof(f));
}
signed main(){
	int T;
	scanf("%lld",&T);
	while(T--){
		clear();
		int n;
		scanf("%lld",&n);
		for(int i=1;i<n;i++){
			int x,y;
			scanf("%lld%lld",&x,&y);
			add(x,y);
		}
		int op;
		scanf("%lld",&op);
	    if(op==0){
		    for(int i=1;i<=n;i++){
		      	scanf("%lld",&a[i]);  
			}
			dfs1(1,0,0);
			dfs2(1,0);
			for(int i=1;i<=n;i++){
				printf("%lld ",f[i]);
			}
			cout<<endl;
		}
		else{
			for(int i=1;i<=n;i++){
				scanf("%lld",&b[i]);
			}
			dfs_1(1,0);
			size[1]=(2*b[1]-k_)/sum_;
			for(int i=1;i<=n;i++){
				cout<<(sum[i]*size[1]+k[i])/2<<" ";
			}
			cout<<endl; 
		}
		
	}
}

现学的卡特兰数。
对于 \(typ=0\) 可以用组合数 \(C(n,i)*C(i,i/2)*C((n-i),(n-i)/2)\)
对于 \(typ=1\) 纯纯卡特兰数 \(catalan(n)=C(2n,n)-C(2n,n-1)\) 这里为\(n/2\) 的卡特兰。
对于 \(typ=3\) 就是横的卡特兰再乘上纵的卡特兰。
对于 \(typ=2\) 就要考虑dp \((n^2)\)\(f[i]\) 表示走 \(i\) 步回到原点的方案数,枚举第一次回到原点时走过的步数 \(j\) (为了存在合法解,j为偶数),则此时方案数为 \(f[i-j]*catalan((j-1)/2)\) 注意是第一次回到。

点击查看代码
#include<iostream>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,op;
int f[100050];
int dp[100050];
int mgml(int x,int p){
	int ans=1;
	while(p){
		if(p&1) ans=(ans*x)%mod;
		x=(x*x)%mod;
		p>>=1;
	}
	return ans;
}
void init(){
	f[0]=1;
	for(int i=1;i<=n;i++){
		f[i]=(f[i-1]*i)%mod;
	}
}
int c(int a,int b){
	return f[a]*mgml(f[a-b],mod-2)%mod*mgml(f[b],mod-2)%mod;
}
int ctl(int n){
	return (c(2*n,n)-c(2*n,n+1)+mod)%mod;
}
signed main(){
	cin>>n>>op;
	init();
	if(op==0){
		int ans=0;
		for(int i=0;i<=n;i++){
			if(i%2) continue;
			ans=(ans+c(n,i)%mod*c(i,i/2)%mod*c(n-i,(n-i)/2)%mod)%mod;
		}
		cout<<ans<<endl;
	}
	else if(op==1){
		cout<<ctl(n/2)<<endl;
	}
	else if(op==2){
		dp[0]=1;
		for(int i=2;i<=n;i+=2){
			for(int j=2;j<=i;j+=2){
				dp[i]=(dp[i]+dp[i-j]*ctl((j-2)/2)%mod*4%mod)%mod;
			}
		}
		cout<<dp[n]<<endl;
	}
	else{
		int ans=0,sum=0;
		for(int i=0;i<=n;i++){
			if(i%2) continue;
			ans=(ans+c(n,i)*(c(i,i/2)-c(i,i/2+1)+mod)%mod*(c(n-i,(n-i)/2)-c(n-i,(n-i)/2+1)+mod)%mod)%mod;
		}
		cout<<ans<<endl;
	}
}
posted @ 2023-07-17 18:40  _bloss  阅读(19)  评论(0)    收藏  举报