這是好幾天前測的了,就不寫總結了,寫下題解吧
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;
}