[CSP-S模拟测试76]题解
咕咕咕
A.序列
无解情况:$n>a*b$或$n<a+b-1$
把序列分成B段,每段内部上升,各段分界处构成下降子序列。
实现并不是太简单,要动态地考虑一下边界什么的。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int T,n,a,b; void work() { scanf("%d%d%d",&n,&a,&b); if(n>1LL*a*b||n<a+b-1) { puts("No"); return ; } puts("Yes"); int beg=n-a+1; for(int i=beg;i<=n;i++) printf("%d ",i); if(b==1)return ; b--; int bl=(beg-1)/b,now=beg-bl; while(b) { bl=(beg-1)/b; now=beg-bl; for(int i=now;i<beg;i++) printf("%d ",i); beg=now; b--; } putchar('\n'); } int main() { scanf("%d",&T); while(T--)work(); return 0; }
B.购物
按套路来讲,答案区间应该是连续的?
并不是。如果把$a[]$排序后求前缀和,会发现如果$\frac{a_i}{2} > sum_{i-1}$,那么$(sum_{i-1},\frac{a_i}{2}]$就是一段缺口。因为已经排过序了,所以这段缺口是无法用其它方式补上的。
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int N=1e5+5; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } int n; ll a[N],sum[N],ans; ll div2(ll x) { return (x-1>>1)+1; } int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(); sort(a+1,a+n+1); for(int i=1;i<=n;i++) { if(div2(a[i])>sum[i-1])ans+=div2(a[i])-sum[i-1]-1; sum[i]=sum[i-1]+a[i]; } cout<<sum[n]-ans<<endl; return 0; }
C.计数
没有限制的话就是Catalan数。
设$dp[l][r]$为在前序遍历上的区间为$[l,r]$,以$l$为根的子树中的方案数。
用二维前缀和记录限制,枚举左右子树分配的大小转移即可。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } typedef long long ll; const ll mod=1e9+7; const int N=405; int T,n,m,g[N][N],sum[N][N]; ll dp[N][N]; int get(int x,int y,int xx,int yy) { return sum[xx][yy]-sum[x-1][yy]-sum[xx][y-1]+sum[x-1][y-1]; } void work() { n=read();m=read(); memset(g,0,sizeof(g)); memset(sum,0,sizeof(sum)); memset(dp,0,sizeof(dp)); for(int i=1;i<=m;i++) { int x=read(),y=read(); g[x][y]++; } for(int i=1;i<=n;i++) { dp[i][i]=1; for(int j=1;j<=n;j++) sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+g[i][j]; } for(int len=2;len<=n;len++) { for(int i=1;i+len-1<=n;i++) { int j=i+len-1; if(!get(i,i+1,i,j))(dp[i][j]+=dp[i+1][j])%=mod; if(!get(i+1,i,j,i))(dp[i][j]+=dp[i+1][j])%=mod; for(int k=i+1;k<=j-1;k++) if(!get(i,i+1,i,k)&&!get(k+1,i,j,k)) (dp[i][j]+=dp[i+1][k]*dp[k+1][j])%=mod; } } printf("%lld\n",dp[1][n]); return ; } int main() { T=read(); while(T--)work(); return 0; }
兴许青竹早凋,碧梧已僵,人事本难防。