fjwc2015题解

来源:互联网 发布:淘宝新店多久有流量 编辑:程序博客网 时间:2024/06/14 09:45

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

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


0 0