【刷题】Grab the Seat!
题目地址:C-Grab the Seat!_"蔚来杯"2022牛客暑期多校训练营1 (nowcoder.com)
题目类型:涉及几何的枚举优化
题目思路:
//涉及几何的枚举优化
//直接在x-y坐标系下画图理解
//先判断连线后,每个人能够挡住的范围是哪一块
//每一列,第二个人必被第一个人挡住,以此类推
//问题简化为:求每一列 没有被挡住的最后一个人
//且,可以将每个人能挡住的右边三角形,作水平辅助线,拆分成右上方三角形和右下方三角形
//对于右上角的三角形
//从第m列,向上计算,
//首先是斜率最大的那一条,确定为j = [(i+k-2)/k],还有判断符合条件的同学(第i列)
//更新mn[i] = min(j,d_x);
bug记录:
(1)
计算斜率中,因为可能有误差,所以最好用int类型dx,dy保存
且这样方便取模运算
比如k' > k 就可以写成 dy' * dx > dy * dx
但是注意数据范围,这样可能会爆int 最好写成 1LL * dy' * dx > 1LL * dy * dx
(2)结果mn改成long long 类型之后,sum居然忘了改类型
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=2e5+10; struct node { int x,y; }d[N]; int pre[N]; //pre[i]表示第i列,x坐标最小的同学,的x坐标 int n,m,k,q; //已经坐下了k个人,有q个换座位事件发生 ll mn[N]; //mn[i]表示第i列不能看见讲台的第一名同学,坐在第mn[i]行 ll sum=0; //表示目前剩下多少个好位置 void input() { for(int i=1;i<=k;i++) scanf("%d%d",&d[i].x, &d[i].y); } void prepare() { for(int i=1;i<=m;i++) pre[i]=mn[i]=n+1; for(int i=1;i<=k;i++) if( pre[d[i].y] > d[i].x ) pre[d[i].y] = d[i].x ; } void calculate() { //先处理右上三角形 int dy=0,dx=1; for(int i=1;i<=m;i++) { if(i==1 ) { mn[i] = pre[i]; continue; } if( 1LL*(i-1)*dx > 1LL*dy*pre[i] ) dx=pre[i],dy=i-1; mn[i] = (1LL*(i-1)*dx + dy-1) / dy ; //目前的mn[i]表示第一个被上三角形挡住的座位,需要通过取min更新成被三角形挡住的座位 } //在处理右下三角形 dy=0,dx=1; for(int i=m;i>0;i--) { if(i==m ) { mn[i] = min(mn[i],1LL*pre[i]); continue; } if( 1LL*(i-m)*dx < 1LL*dy*pre[i] ) dx=pre[i],dy=i-m; mn[i] = min( mn[i] , (1LL*(i-m)*dx + dy+1) / dy ); //取模运算,因为dy小于0 //所以ceil是选择+dy+1, } sum=0; for(int i=1;i<=m;i++) sum += mn[i]-1; } int main() { scanf("%d%d%d%d",&n,&m,&k,&q); input(); while(q--)//200次调整,每次全部重新计算就行 { int pos; scanf("%d",&pos); scanf("%d%d",&d[pos].x ,&d[pos].y); prepare(); calculate(); printf("%d\n",sum); } return 0; }