2018.8.18提高B组模拟试题
今天出题人出了三道看似很难,实则暴力就能过的题...我...
T1 题意简述:jzoj3452
解题思路:这题...看似是一道dp...实则是一道结论题...
可以发现矩形数量最多当且仅当石子满足以下两种摆放顺序之一:
XXX...XXX 或 XXX...XXXXX
XXX...XXX XXX...XXX
... ...
XXX...XXX XXX...XXX
XX
因此只需枚举除最后一行(列)外每行(列)有几个石子,然后计算即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define ll long long using namespace std; ll n,m,k,ans; int main() { freopen("rectangle.in","r",stdin); freopen("rectangle.out","w",stdout); scanf("%lld%lld%lld",&n,&m,&k); for(ll i=2;i<=m;i++) { ll x=k/i,y=k%i; if(!x) break; if(x>n||(x==n&&y)) continue; ll tmp=x*(x-1)/2*i*(i-1)/2+y*(y-1)/2*x; ans=max(ans,tmp); } for(ll i=2;i<=n;i++) { ll x=k/i,y=k%i; if(!x) break; if(x>m||(x==m&&y)) continue; ll tmp=x*(x-1)/2*i*(i-1)/2+y*(y-1)/2*x; ans=max(ans,tmp); } printf("%lld\n",ans); return 0; }
T2 题意简述:jzoj3453
解题思路:这道题我的第一反应是先找图上的简单环,然后枚举每条被删除的边看是否能每个环分到一
条边或分不到边,多余的未分配边数即等于增加的连通块数。
虽然想法是完全正确的,但是找简单环可一点也不容易...到考试结束我也没写出来。
看过题解,有种想吐血的冲动。题解给出的方法如下:
记录两个并查集数组分别表示加入前i条边与后i条边后的连通块情况。询问时合并即可。
...我还是太蠢了。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,m,k,fa[501],prefa[10002][501],suffa[10002][501]; struct uio{ int a,b; }edge[10001]; int getprefa(int x,int id) { if(x==prefa[id][x]) return x; return prefa[id][x]=getprefa(prefa[id][x],id); } int getsuffa(int x,int id) { if(x==suffa[id][x]) return x; return suffa[id][x]=getsuffa(suffa[id][x],id); } int getfa(int x) { if(x==fa[x]) return x; return fa[x]=getfa(fa[x]); } int main() { freopen("connect.in","r",stdin); freopen("connect.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) prefa[0][i]=suffa[m+1][i]=i; for(int i=1;i<=m;i++) { scanf("%d%d",&edge[i].a,&edge[i].b); memcpy(prefa[i],prefa[i-1],sizeof(prefa[i-1])); int xx=getprefa(edge[i].a,i); int yy=getprefa(edge[i].b,i); if(xx!=yy) prefa[i][yy]=xx; } for(int i=m;i;i--) { memcpy(suffa[i],suffa[i+1],sizeof(suffa[i+1])); int xx=getsuffa(edge[i].a,i); int yy=getsuffa(edge[i].b,i); if(xx!=yy) suffa[i][yy]=xx; } scanf("%d",&k); for(int i=1;i<=k;i++) { int u,v; scanf("%d%d",&u,&v); memcpy(fa,prefa[u-1],sizeof(prefa[u-1])); for(int j=1;j<=n;j++) { int xx=getfa(j); int yy=getsuffa(j,v+1); int zz=getfa(yy); if(xx!=zz) fa[xx]=zz; } int tmp=0; for(int j=1;j<=n;j++) if(getfa(fa[j])==j) tmp++; printf("%d\n",tmp); } return 0; }
T3 题意简述:jzoj3450
解题思路:各位大佬猜这道题的正解是什么?
揭晓答案:暴力dfs+一个愚蠢的剪枝。
...这种题目出现在提高B组真的好吗?
至于那个愚蠢的剪枝,为了不浪费各位大佬的时间,博主稍微解释一下:
不必每搜一个点就把vis数组清空一次,只需把标记打成这个点的标号即可。
...足够愚蠢吧?
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,m,k,ans,flg,a[501][501],vis[501][501]; void dfs(int x,int y,int num,int id) { vis[x][y]=id; if(a[x][y]>num) {flg=1;return;} if(x!=1&&a[x-1][y]>num-k&&vis[x-1][y]!=id) dfs(x-1,y,num,id); if(flg) return; if(x!=n&&a[x+1][y]>num-k&&vis[x+1][y]!=id) dfs(x+1,y,num,id); if(flg) return; if(y!=1&&a[x][y-1]>num-k&&vis[x][y-1]!=id) dfs(x,y-1,num,id); if(flg) return; if(y!=m&&a[x][y+1]>num-k&&vis[x][y+1]!=id) dfs(x,y+1,num,id); if(flg) return; } int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { flg=0; dfs(i,j,a[i][j],(i-1)*m+j); if(!flg) ans++; } printf("%d\n",ans); return 0; }