【2019.10.8】备战CSP2019模拟赛
总结
分少因为正解打得少。
分不够多因为暴力打得不够多。
(到位)
T1.方阵
题意:
n*m的方阵,第i列j行的值为Xij,给定几个矩形的左上角和右下角,问SUM/ MAX/ MIN(询问的矩形较长边不会大于两倍较短边)?
题解:
我是打的前缀和求SUM,但是遇到MAX和MIN就直接跪了,没想到打RMQ,打的枚举,拿了40暴力分。
正解矩阵求和用前缀和,矩阵最值二维RMQ维护,特殊处理下矩阵询问,转变为询问两个正方形,保证空间合法。
f[i][j][k]表示左上角顶点坐标为(i, j),边长为2的k次方的正方形。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int N=801; string s; int a[N][N],sum[N][N],f[N][N][10],g[N][N][10],p[11]; inline int read() { int X=0,w=1; char ch=0; while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();} while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar(); return X*w; } inline int write(int x) { if(x>9) write(x/10); putchar(x%10+'0'); } inline int max(int x,int y) { return x>y?x:y; } inline int min(int x,int y) { return x<y?x:y; } int main(){ freopen("phalanx.in","r",stdin); freopen("phalanx.out","w",stdout); int n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { f[i][j][0]=g[i][j][0]=a[i][j]=read(); sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]; } for(int i=p[0]=1;i<=10;i++) p[i]=p[i-1]<<1; for(int k=1;k<10;k++) for(int i=1;i<=n && i+p[k]-1<=n;i++) for(int j=1;j<=m && j+p[k]-1<=m;j++) { f[i][j][k]=max(f[i][j][k-1],f[i][j+p[k-1]][k-1]); f[i][j][k]=max(f[i][j][k],max(f[i+p[k-1]][j][k-1],f[i+p[k-1]][j+p[k-1]][k-1])); g[i][j][k]=min(g[i][j][k-1],g[i][j+p[k-1]][k-1]); g[i][j][k]=min(g[i][j][k],min(g[i+p[k-1]][j][k-1],g[i+p[k-1]][j+p[k-1]][k-1])); } int q=read(); while(q--) { cin>>s; int x1=read()+1,y1=read()+1,x2=read()+1,y2=read()+1; if(s=="SUM") { write(sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]),putchar('\n'); continue; } int ans=s=="MIN"?3000:0,z=min(log2(x2-x1+1),log2(y2-y1+1)); if(s=="MAX") { for(int yy=y1;yy+p[z]<=y2;yy+=p[z]) ans=max(ans,max(f[x1][yy][z],f[x2-p[z]+1][yy][z])); for(int xx=x1;xx+p[z]<=x2;xx+=p[z]) ans=max(ans,max(f[xx][y1][z],f[xx][y2-p[z]+1][z])); ans=max(ans,max(f[x1][y1][z],f[x1][y2-p[z]+1][z])); ans=max(ans,max(f[x2-p[z]+1][y1][z],f[x2-p[z]+1][y2-p[z]+1][z])); }else { for(int yy=y1;yy+p[z]<=y2;yy+=p[z]) ans=min(ans,min(g[x1][yy][z],g[x2-p[z]+1][yy][z])); for(int xx=x1;xx+p[z]<=x2;xx+=p[z]) ans=min(ans,min(g[xx][y1][z],g[xx][y2-p[z]+1][z])); ans=min(ans,min(g[x1][y1][z],g[x1][y2-p[z]+1][z])); ans=min(ans,min(g[x2-p[z]+1][y1][z],g[x2-p[z]+1][y2-p[z]+1][z])); } write(ans),putchar('\n'); } return 0; }
T2.合照
题意:
给出n个数,有一些特定要求,形如x,y表示x一定要在y的左边,求排列方案数。
题解:
第一眼想序列做法,失败。第二眼考虑状压DP,时间复杂度不够优秀,可能还要加优化(可是我想不到优化方法)。看完T3再回头,可以连边...树形DP??然而敲到最后都没时间打T3的白送20分了还是敲错了只拿到40分。部分分的做法不一定是正解的暴力。(实在想不到优化八成就不是)。
“首先有一个条件就是每一个人最多提出一个要求。
那么连边后的dag是一个森林,不相交的n棵树。
g[x]*=g[v]*C(size[x],size[v]);
表示以x为根的方案数,这个用乘法原理很好理解,每一颗子树是独立的。
然后答案也是一样的,相当于有一个超级根把所有树连在一起,所以就有:
ans*=g[i]*C(sz,size[i]);,同理,也是独立的,直接乘就好。 ”
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<algorithm> #include<cstring> #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int N=2e5+5; int head[N],next[N],go[N],size[N],d[N]; int tot,fac[N],inv[N],n,m,mo,f[N],g[N]; inline void add(int x,int y) { go[++tot]=y; next[tot]=head[x]; head[x]=tot; } inline int find(int x) { if (f[x]==x) return x; else return f[x]=find(f[x]); } inline int C(int n,int m) { return 1ll*fac[n]*inv[m]%mo*inv[n-m]%mo; } inline void dfs(int x) { g[x]=1;size[x]=0; for (int i=head[x];i;i=next[i]) { int v=go[i]; dfs(v); size[x]+=size[v]; g[x]=1ll*g[x]*g[v]%mo*C(size[x],size[v])%mo; } size[x]++; } int main() { freopen("photo.in","r",stdin); freopen("photo.out","w",stdout); int cas; scanf("%d",&cas); while(cas--) { scanf("%d%d%d",&n,&m,&mo); fac[0]=inv[0]=fac[1]=inv[1]=1; fo(i,2,n) fac[i]=1ll*fac[i-1]*i%mo,inv[i]=1ll*(mo-mo/i)*inv[mo%i]%mo; fo(i,2,n) inv[i]=1ll*inv[i]*inv[i-1]%mo; fo(i,1,n)f[i]=i,head[i]=d[i]=0; int flag=0;tot=0; while (m--) { int x,y; scanf("%d%d",&x,&y); if (find(x)!=find(y))f[find(x)]=find(y); else flag=1; add(y,x); d[x]++; } if (flag) { printf("0\n"); continue; } int ans=1,sz=0; fo(i,1,n) if (!d[i]) { dfs(i); sz+=size[i]; ans=1ll*ans*g[i]%mo*C(sz,size[i])%mo; } printf("%d\n",ans); } }
T3.
想到一个很不优的DP思路,一看就会超时, 没打,想着打完T2回头拿个20分暴力滚,转头肝T2去了。结果有人打了还拿了80分(加了优化)...所以我还是要加快肝题速度啊,才有时间打暴力。