这是好几天前测的了,就不写总结了,写下题解吧
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;
}