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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章