1. 程式人生 > 實用技巧 >題解 CF555C 【Case of Chocolate】

題解 CF555C 【Case of Chocolate】

思路:

\(\quad\)說實話,第一眼看這題想到的是線段樹,但無奈 \(1<=n<=10^ 9\) ,離散化有點複雜,動態開點又不會,所以使用了一種離線做法,跑的還賊快(大霧

\(\quad\)現將所有操作存起來, \(dir\) 表示方向, \(1\) 為上, \(0\) 為左,然後按 \(x\) 為第一關鍵字, \(t\) (時間)為第二關鍵字排序(我第一次 \(WA\) 就是這個原因)

struct node{
  int x,y,t,dir;
}a[N],b[N];
il bool cmp(node a1,node a2){return a1.x==a2.x?a1.t<a2.t:a1.x<a2.x;}

  for(re i=1,x,y;i<=m;i++)
    {
      x=read(),y=read();ch=getchar();
      if(ch=='U')a[i]=(node){x,y,i,1};
      else a[i]=(node){x,y,i,0};
     }
  sort(a+1,a+m+1,cmp);

\(\quad\)然後考慮每一種情況,顯然,向左的操作會被前面的向上的操作影響(前面的指 \(x\) 小於 \(TA\) ,且時間 \(t\) 也小於 \(TA\) 的),向上的操作會被後面的向左的操作影響(後面指 \(x\) 大於 \(TA\) ,但時間 \(t\) 小於 \(TA\) 的),所以可以發現相鄰的向上的操作和向左的操作會互相影響(相鄰是指x相鄰),這時候我們就要判斷它們的時間先後了。


\(\quad\)然後我們就可以發現只有向上的操作在前,才有可能對後面的向左的操作產生影響(先不考慮時間),另外向上的也只被後面的第一個向左的所影響,所以向上的暫時無法得出答案,先放進一個棧中儲存,只有遇到向左的就把棧中的時間大於 \(TA\)

的更新答案,遇到一個時間小於 \(TA\) 的就被堵住了,這時候就更新它自己的答案。(這裡的 \(TA\) 指的是向左的操作)

\(\quad\)現在來模擬一個數據來理解一下。

\(\quad\)先排序,對於操作 \(4\) ,此時棧是空的,直接更新答案,對於操作 \(6\) 、操作 \(1\) 和操作 \(5\) ,直接存進棧裡,等到操作 \(3\) 時開始退棧,先比較棧頂的時間,更新操作 \(5\) 的時間,將 \(5\) 退出,然後和操作 \(1\) 比較,停止退棧,更新操作 \(3\) 本身的答案,最後將操作 \(2\) 壓進棧中,迴圈結束,將棧中元素全部退出,這些把一列的巧克力都吃光了。

\(\quad\)如果還不理解就看看完整程式碼吧,附帶註釋。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define re register int
#define int long long
#define il inline
il int read()
{
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)&&ch!='-')ch=getchar();
  if(ch=='-')f=-1,ch=getchar();
  while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
  return x*f;
}
il void print(int x)
{
  if(x<0)putchar('-'),x=-x;
  if(x/10)print(x/10);
  putchar(x%10+'0');
}
const int N=2e5+5;
int n,m,ans[N],tot;
struct node{
  int x,y,t,dir;
}a[N],b[N];
il bool cmp(node a1,node a2){return a1.x==a2.x?a1.t<a2.t:a1.x<a2.x;}//注意
signed main()
{
  n=read();m=read();char ch;
  for(re i=1,x,y;i<=m;i++)
    {
      x=read(),y=read();ch=getchar();
      if(ch=='U')a[i]=(node){x,y,i,1};//dir=1表示向上,Up
      else a[i]=(node){x,y,i,0};//dir=0表示向左,Left
    }
  sort(a+1,a+m+1,cmp);//排序
  b[0]=(node){0,n+1,0,1};//處理邊界
  for(re i=1;i<=m;i++)
    {
      if(a[i].x==a[i-1].x&&a[i].y==a[i-1].y)continue;//如果遇到相同的就跳過,後面的一個巧克力都吃不到
      if(a[i].dir)b[++tot]=a[i];
      else {
	while(b[tot].t>a[i].t){//條件:時間更大
	  ans[b[tot].t]=b[tot].y-a[i].y;//更新答案
	  tot--;//退棧
	}
	ans[a[i].t]=a[i].x-b[tot].x;//不能再退棧時,更新自己的答案
      }
    }
  while(tot){
    ans[b[tot].t]=b[tot].y;//將還在棧裡的退出來
    tot--;
  }
  for(re i=1;i<=m;i++)print(ans[i]),putchar('\n');
  return 0;
}