fjwc2015题解

这是好几天前测的了,就不写总结了,写下题解吧

T1:

http://hzwer.com/6297.html

题目意思就不说了,这题可以发现l[i]不超过5000,于是记s1[i][j]代表用1到j这j种颜色拼出i的方案数,s2[i][j]代表用任意j种颜色拼出i的方案数

那么设f[i][j]代表第i层用了j种颜色的方案数,g[i]=sigma(f[i][j]),有f[i][j]=g[i-1]*s2[l[i]][j]-f[i-1][j]*s1[i][j],于是就Ok了

#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
#define PB push_back
using namespace std;
typedef long long LL;
const int maxn=1000011,maxl=5011;
int a[maxn];
int ma,n,m,p;
void init(){
	scanf("%d%d%d",&n,&m,&p); ma=0;
	for (int i=1;i<=n;++i) scanf("%d",a+i),ma=max(ma,a[i]);
}
int f[2][maxl]; int g[2];
int s1[maxl][maxl],s2[maxl][maxl];
void work(){
	s1[0][0]=s2[0][0]=1;
	for (int i=1;i<=ma;++i){
		int quit=min(i,m);
		for (int j=1;j<=quit;++j)
			s1[i][j]=((LL)s1[i-1][j]*(j-1)+(LL)s1[i-1][j-1]*j)%p,
			s2[i][j]=((LL)s2[i-1][j]*(j-1)+(LL)s2[i-1][j-1]*(m-j+1))%p;
//			cout<<i<<' '<<j<<' '<<s1[i][j]<<' '<<s2[i][j]<<endl;
	}
	g[0]=1; a[0]=0; int now=0,last=1;
	for (int i=1;i<=n;++i){
		now^=1; last^=1;
		int quit=min(a[i-1],a[i]);
		for (int j=1;j<=quit;++j)
			f[now][j]=((LL)g[last]*s2[a[i]][j]-(LL)f[last][j]*s1[a[i]][j])%p;
		for (int j=quit+1;j<=a[i];++j)
			f[now][j]=((LL)g[last]*s2[a[i]][j])%p;
//		for (int j=1;j<=a[i];++j) cout<<i<<' '<<j<<' '<<f[i][j]<<endl;
		g[now]=0; for (int j=1;j<=a[i];++j) g[now]=(g[now]+f[now][j])%p;
	}
	cout<<(g[now]%p+p)%p<<endl;
}
int main(){
	freopen("christmas.in","r",stdin); freopen("christmas.out","w",stdout);
	init(); work();
	fclose(stdin); fclose(stdout);
	return 0;
}

T2:

http://hzwer.com/6301.html

首先这题把小的都画出来,可以发现除了6的都是有单调性的(这个也不证明,但是感性理解一下就会觉得很自然),然后问题变成求深度差为i的最小是多少个点组成,这个可以构造的,设f[0]为空,f[1]为一个点,   f[i]为一个点,其左子树是f[i-1],右子树数f[i-2],那么f[2*i+1]就是最小的深度差为i的树,那么f[i]的点数也很好求,不过这题数据组数巨多,可以先把f[i]存下来,不过空间不够,就分块存(比如隔20个存两个)这样每组数据就比较快了

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10010,Len=20,maxm=50011;
//long long cnt=0;
struct bign{
	static const int maxbit=1300,width=9,limit=1000000000;
	int len,a[maxbit];
	void clear(){len=1; memset(a,0,sizeof(a));}
	void delete0(){while (len>1 && !a[len-1]) --len;}
	void init(int x){clear(); a[0]=x%limit; a[1]=x/limit; len=2; delete0();}
	void init(char *s){
		clear();
		int n=strlen(s); len=0;
		for (int i=n-1;i>=0;i-=width){
			int x=0;
			for (int j=max(0,i-width+1);j<=i;++j) x=x*10+s[j]-'0';
			a[len++]=x;
		}
		if (len==0) len=1;
	}
	bign &operator +=(bign &b){
		len=max(len,b.len)+1; //cnt+=len;
		for (int i=0;i<len;++i){
			a[i]+=b.a[i];
			if (a[i]>=limit){
				a[i]-=limit; ++a[i+1];
			}
		}
		delete0(); return *this;
	}
	void inc(){
		++len;
		for (int i=0;i<len;++i){
			++a[i]; if (a[i]!=limit) break; a[i]=0;
		}
		delete0();
	}
	bool operator <(bign &b){
		delete0(); b.delete0();
		if (len!=b.len) return len<b.len;
		for (int i=len-1;i>=0;--i)
			if (a[i]!=b.a[i]) return a[i]<b.a[i];
		return 0;
	}
	void write(){
		printf("%d",a[len-1]);
		for (int i=len-2;i>=0;--i) printf("%0*d",width,a[i]);
		puts("");
	}
};
bign &operator +(bign a,bign b){return a+=b;}

bign n; int f[maxn]; char s[100000];
void init(){
	scanf("%s",s); n.init(s);
}
bign a[Len+3];
bign b1[maxm/Len+11],b2[maxm/Len+11];
void prepare(){
	a[1].init(1); a[0].init(2);
	b1[1]=a[1]; b2[1]=a[0];
	for (int i=3;i<maxm;++i){
		a[i&1]+=a[(i&1)^1]; a[i&1].inc();
		if (i%Len==2) b1[i/Len+1]=a[(i&1)^1],b2[i/Len+1]=a[i&1];
	}
}
void work(){
	if ((n.len==1) && (n.a[0]==6 || n.a[0]<=3)){puts("0"); return;}
	if (n.len==1 && n.a[0]<=10){puts("1"); return;}
	int t=1,w=maxm/Len;
	while (t<=w){
		int mid=(t+w)>>1;
		if (n<b2[mid]) w=mid-1;
		else t=mid+1;
	}
	--t;
	a[1]=b1[t]; a[2]=b2[t];
	for (int i=3;;++i){
		a[i]=a[i-1]+a[i-2]; a[i].inc();
		if (n<a[i]){printf("%d\n",((t-1)*Len+i)/2-1); return;}
	}
}
int main(){
	freopen("world.in","r",stdin); freopen("world.out","w",stdout);
	prepare();
	int T; scanf("%d",&T);
	while (T--) init(),work();
	fclose(stdin); fclose(stdout);
	return 0;
}


T3:

http://hzwer.com/6306.html

这题看起来就比较神,还是膜拜了hzwer的题解才搞出来的,首先把行和列都从小到大排序,自然不影响答案,然后在对上限相同的一块考虑,发现是一个十字型的右边和下边,且每行每列都要有一个这么大的,于是先把所有的可以填的方案加上,然后枚举有几行几列没有那么大的,把他们去掉,变成一个子问题,减掉即可

#include<cmath>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=55,mod=1000000009;
int a[10011],b[10011],n,m;
void init(){
	scanf("%d",&n); int x;
	for (int i=1;i<=n;++i) scanf("%d",&x),++a[x];
	scanf("%d",&m);
	for (int i=1;i<=m;++i) scanf("%d",&x),++b[x];
}
int power(int x,int t){
	int res=1;
	for (;t;t>>=1,x=(1LL*x*x)%mod)
		if (t&1) res=(1LL*res*x)%mod;
	return res;
}
int dp[maxn][maxn],C[maxn][maxn];
int f(int n,int m,int a,int b,int h){
	if (!a && !b) return 1;
	if (dp[a][b]!=-1) return dp[a][b];
	int res=power(h+1,n*m-(n-a)*(m-b));
	for (int i=0;i<=a;++i) for (int j=0;j<=b;++j) if (i || j)
		res=(res+(mod-1LL*C[a][i]*C[b][j]%mod*
		power(h,(i*m+n*j-i*j))%mod*f(n-i,m-j,a-i,b-j,h)%mod))%mod;
	dp[a][b]=res; return res;
}
void work(){
	C[0][0]=1;
	for (int i=1;i<=n;++i){
		C[i][0]=1;
		for (int j=1;j<=i;++j)
		C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	}
	int ans=1;
	for (int i=1;i<=10000;++i){
		a[i]+=a[i-1]; b[i]+=b[i-1];
		if ((a[i]-a[i-1]==0) && (b[i]-b[i-1]==0)) continue;
		memset(dp,255,sizeof(dp));
//		if (n-a[i-1] || m-b[i-1])
/*		cout<<"QueryaF "<<n-a[i-1]<<' '<<m-b[i-1]<<' '<<a[i]-a[i-1]<<' '<<b[i]-b[i-1]<<' '<<i<<
		' '<<f(n-a[i-1],m-b[i-1],a[i]-a[i-1],b[i]-b[i-1],i)<<
		endl;*/
		ans=(1LL*ans*f(n-a[i-1],m-b[i-1],a[i]-a[i-1],b[i]-b[i-1],i))%mod;
	}
	cout<<ans<<endl;
}
int main(){
	freopen("ensconce.in","r",stdin); freopen("ensconce.out","w",stdout);
	init(); work();
	fclose(stdin); fclose(stdout);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值