别问我为何过去一周了才写博客。。。node
没有作出最后一题,是我失了智。数组
这一周原本说好的计组考试结果收到的通知是计网,可怜我计组复习了两周,计网彻底没有复习(预习)。优化
周四终于考完本学期第一场考试,结果咱们班的现场答卷状况让咱们信安教研室主任勃然大怒。。spa
今天上午8点半要去实验室作密码学课程设计,结果睡过头被室友锁宿舍一上午,,,因而,,因而在宿舍颓废了一天。不行,我这么好的学生怎么能颓废呢,好吧,是个人良心在痛,晚上拉上室友图书馆复习,准备明天的校赛!设计
前三场都没打好,只能靠这场碰碰运气看看能不能晋级。额,维森莫是前三场,初赛前两场去西安参加邀请赛被秦龙酒店坑了一把(去过都知道)。因此从第四场开始打的。code
急于过题觉得只有三种状况,结果WA了一发,仔细看题原来是我失了智。这么久没打代码,遇到这种shabi模拟题居然有点焦头烂额。blog
int main() { int n; while(~scanf("%d",&n)) { printf("+-----+\n"); if(n<20) printf("| E|\n"); else if(n>=20&&n<60) printf("|- E|\n"); else if(n>=90) printf("|- 4G|\n"); else printf("|- 3G|\n"); n-=20; for(int i=2; i<=5; i++) { if(n>=20) { printf("|"); for(int j=1; j<=i; j++) printf("-"); for(int j=i+1; j<=5; j++) printf(" "); printf("|\n"); } else printf("| |\n"); n-=20; } printf("+-----+\n"); } return 0; }
这么简单的题我居然直接想到shabi搜索上去了,结果初始值没赋好还WA了两发,啊啊啊,石乐志。ip
额,中文题就不介绍题意了。ci
const int N=1e6+5; int a[55][55],x[55],y[55],vis[55][55]; int dir[5][2]= {{-1,0},{1,0},{0,1},{0,-1}}; int n,k,m; struct node { int x,y; }; int bfs(int i,int j) { memset(vis,0,sizeof(vis)); node tmp; tmp.x=x[i]; tmp.y=y[i]; queue<node>q; vis[x[i]][y[i]]=0; q.push(tmp); while(!q.empty()) { tmp=q.front(); q.pop(); if(tmp.x==x[j]&&tmp.y==y[j]) break; for(int i=0; i<4; i++) { int xx=tmp.x+dir[i][0]; int yy=tmp.y+dir[i][1]; if(a[xx][yy]&&!vis[xx][yy]) { vis[xx][yy]=vis[tmp.x][tmp.y]+1; node tmpp; tmpp.x=xx; tmpp.y=yy; q.push(tmpp); } } } return vis[x[j]][y[j]]; } int main() { while(~scanf("%d%d",&n,&k)) { memset(a,0,sizeof(a)); int h; for(int i=1; i<=n; i++) { scanf("%d",&h); for(int j=1; j<=h; j++) a[i][j]=1; } scanf("%d",&m); for(int i=1; i<=m; i++) scanf("%d%d",&x[i],&y[i]); int ans=0; for(int i=1; i<=m; i++) for(int j=i+1; j<=m; j++) { if(bfs(i,j)<=k) { ans++; // printf("%d %d\n",i,j); } } printf("%d\n",ans); } return 0; }
看了这个题的数据范围我才意识到本身被简单版的数据范围坑了,明明有更简单的方法却往复杂的方向走。。
结合题意观察到横向距离是不变的(两栋楼横坐标之差),而纵向距离只需枚举看是否有一条通道链接这两栋楼,咱们开个数组标记一下就行了。关键要看到H最多才20,即空间复杂度1e5*20。枚举任意两栋楼而后暴力判断便可。
const int N=2e5+5; int a[N][21],x[2001],y[2001],vis[N][20],h[N]; int n,k,m; int main() { while(~scanf("%d%d",&n,&k)) { memset(a,0,sizeof(a)); memset(vis,0,sizeof(vis)); h[0]=0; for(int i=1; i<=n; i++) { for(int j=1; j<=h[i-1]; j++) vis[i][j]+=a[i-1][j]; scanf("%d",&h[i]); for(int j=1; j<=h[i]; j++) { a[i][j]=1; vis[i][j]++; } } scanf("%d",&m); for(int i=1; i<=m; i++) scanf("%d%d",&x[i],&y[i]); int ans=0; for(int i=1; i<=m; i++) for(int j=i+1; j<=m; j++) { int tmp=abs(x[j]-x[i]); int h2=max(y[i],y[j]); int f=y[j]+y[i]-2; for(int l=2;l<=h2;l++) if(abs(vis[x[i]][l]-vis[x[j]][l])==tmp) f=min(f,abs(y[i]-l)+abs(y[j]-l)); tmp+=f; if(tmp<=k) ans++; } printf("%d\n",ans); } return 0; }
仍是同样的题意,只不过数据范围变了,注意到和中等版本不一样的是这个题的核心办公室可达2e5。那么真正有趣的来了,暴力枚举任意两栋楼确定是不行的,那么怎么优化呢。我居然想到公共祖先问题上,结果发现解决不了。。套路,又是这个套路,咱们观察到,从左往右只要咱们知道了第一栋楼可行解的范围也就是右端点的位置,那么第二栋楼不就在第一栋楼的基础上再往右贪心便可。大牛可能一眼就明白一个滑动窗便可,然而我等菜鸟居然直接想到二分上了,枚举左端点二分右端点,我怕会超时居然用上次在玲珑杯学到的一个技巧倍增优化。嗯,越想感受越对,在这条路上一去不复返。。到最后也没写出来,好吧,是我菜。讲道理,这个二分套路应该是能够的。不过犯了关键错误是咱们确定要对这些点排序,咱们想到的是按xy排序,后来发现应该按x+y的大小排序,举个例子就懂了,(1,5)到(2,18)和(3,1)。因此应该按x+y排序。清醒后才发现能够直接尺取贪心。。今天写了一波,感受过TLE,然而过了,,,,过了。。。核心判断仍是和第二题同样。
const int N=2e5+5; int a[N][21],vis[N][21],h[N]; int n,k,m; struct node { int x,y; } b[N]; int cmp(node a,node b) { return a.x+a.y<b.x+b.y; /* 不能这样排序,(1,5)到(2,18)和(3,1)之间的距离是不一样的 if(a.x!=b.x) return a.x<b.x; return a.y<b.y; */ } int judge(int i,int j) { int tmp=abs(b[j].x-b[i].x); int h2=max(b[i].y,b[j].y); int f=b[j].y+b[i].y-2; for(int l=2; l<=h2; l++) if(abs(vis[b[i].x][l]-vis[b[j].x][l])==tmp) f=min(f,abs(b[i].y-l)+abs(b[j].y-l)); tmp+=f; return tmp<=k; } int main() { while(~scanf("%d%d",&n,&k)) { memset(a,0,sizeof(a)); memset(vis,0,sizeof(vis)); h[0]=0; ll ans=0; for(int i=1; i<=n; i++) { for(int j=1; j<=h[i-1]; j++) vis[i][j]+=a[i-1][j]; scanf("%d",&h[i]); for(int j=1; j<=h[i]; j++) { a[i][j]=1; vis[i][j]++; } } scanf("%d",&m); for(int i=0; i<m; i++) scanf("%d%d",&b[i].x,&b[i].y); sort(b,b+m,cmp); int j=0; for(int i=0; i<m; i++) { while(j<m&&judge(i,j)) j++;//贪心尺取 int k=j; while(k>=m||!judge(i,k)) k--;//回退,这个操做原本觉得会超时。看来数据不强 ans+=k-i; } printf("%lld\n",ans);//注意数据范围 } return 0; }