codeforces #310 div1 C
操作无论是U还是L,都会使原图形分裂成两个图形,且两个图形的操作互不影响
我们又发现由于操作点只可能在下斜线上,如果将操作按x排序
那么无论是U还是L,都会将操作序列完整分割成两半,且两个操作序列互不影响
这样我们就可以对操作进行分治,每次找到最靠前的操作,并将操作序列分割
对于U操作而言,计算其答案只需要知道当前列最靠下的那一行
对于L操作而言,计算其答案只需要知道当前行最靠右的那一列
分治的时候动态维护即可
注:这样的话最坏情况会递归20w层,在CF上会爆栈,所以我的代码人为的开了栈空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<map> #include<iostream> using namespace std; const int maxn=200010; int n,m; struct OP{ int a,b,id; char c; }A[maxn]; int mn[maxn<<2]; int ans[maxn]; map< int , bool >vis; bool cmp( const OP &A, const OP &B){ return A.a<B.a;} void ch_read( char &ch){ ch= getchar (); while (ch< '!' )ch= getchar (); } int Min( int a, int b){ return A[a].id<A[b].id?a:b;} void build( int o, int L, int R){ if (L==R){mn[o]=L; return ;} int mid=(L+R)>>1; //cout<<mid<<endl; build(o<<1,L,mid); build(o<<1|1,mid+1,R); mn[o]=Min(mn[o<<1],mn[o<<1|1]); } int ask( int o, int L, int R, int x, int y){ if (L>=x&&R<=y) return mn[o]; int mid=(L+R)>>1; if (y<=mid) return ask(o<<1,L,mid,x,y); else if (x>mid) return ask(o<<1|1,mid+1,R,x,y); else return Min(ask(o<<1,L,mid,x,y),ask(o<<1|1,mid+1,R,x,y)); } void Solve( int L, int R, int Low, int Right){ if (L>R) return ; int now=ask(1,1,m,L,R); if (vis[A[now].a]){ Solve(L,now-1,Low,Right); Solve(now+1,R,Low,Right); return ; } vis[A[now].a]= true ; if (A[now].c== 'U' ){ ans[A[now].id]=A[now].b-Low; Solve(L,now-1,Low,Right); Solve(now+1,R,Low,A[now].a); } else { ans[A[now].id]=A[now].a-Right; Solve(L,now-1,A[now].b,Right); Solve(now+1,R,Low,Right); } return ; } int main(){ int __size__ = 20 << 20; // 20MB char *__p__ = ( char *) malloc (__size__) + __size__; __asm__( "movl %0, %%esp\n" :: "r" (__p__)); scanf ( "%d%d" ,&n,&m); for ( int i=1;i<=m;++i){ scanf ( "%d%d" ,&A[i].a,&A[i].b); ch_read(A[i].c);A[i].id=i; } sort(A+1,A+m+1,cmp); build(1,1,m); Solve(1,m,0,0); for ( int i=1;i<=m;++i) printf ( "%d\n" ,ans[i]); return 0; } |
另外附上官方题解:
还是上面的思路,我们很容易发现:
对于每一行,只需要知道其最靠右覆盖的列
对于每一列,只需要知道其最靠下覆盖的行
我们可以建两棵线段树,分别维护行的信息和列的信息
每次操作分别更改两棵线段树即可
又因为n很大,所以我们需要对行和列离散化
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步