【纪中集训2019.3.15】恶熊咆哮

题目

描述

\(n\)只熊,初始时坐标为\((x_i,y_i)\)

这些熊会按照标号依次吼叫,当第\(i\)只熊吼叫,其他熊会移动;

\((x_i,y_i)\)会移动到\((x_i \pm 1,y_i \pm 1)\)离吼叫的熊欧几里得距离最小的位置;

问当第\(i\)只熊生病了,不吼叫也不移动,其他的熊依次吼叫后的\(\sum_{i=1}^{n}x_iy_i\);

依次输出\(ans_i\);

范围

\(2 \le n \le 2.5 \times 10^5 \ , \ 1 \le x_i ,y_i \le 10^6\)

题解

  • $Part  1 $

  • 横纵坐标独立,所以分别考虑每一维的情况;

  • 设将熊按照坐标大小排序并差分,设\(d_i = x_i - x_{i-1},pos_i\)为熊离散后的下标;

  • 那么操作可以看成找到\(pos_i\)之前的第一个不为\(0\)的数字\(--\),找到\(pos_i\)及之后第一个不为\(0\)的数字\(--\);

  • 我们可以找到大家都好好的时候这样子的吼叫区间\((L_i,R_i)\);

  • 容易发现他们是互相包含或者端点相接;

  • 当第\(i\)熊不吼叫时,它减少的贡献初始也是\((L_i,R_i)\),但是 $ i+1 \to m $ 的熊的吼叫区间会影响$ (L_i,R_i) $ ;

  • 可以发现最终\(L_i,R_i\)也是一个区间,只要统计了所有最终这样的区间就可以统计答案;

  • $Part  2 $

  • 考虑第\(j\)只熊吼叫时,​\((L_j,R_j)(j>i)\)依次对​\((L_i,R_i)\)的影响;

    • 当且仅当 $ L_j \le L_i  ,  R_i \le R_j $ 时 $ i $ 会被 $ j $ 影响;
    • 1.若\(R_i<pos_j\),则新的区间为\((L_j,L_i)\);
    • 2.若\(L_i >= pos_j\),则新的区间为\((R_i,R_j)\);
    • 3.若\(L_i<pos_j且pos_j<=R_i\) , 则新的区间为\((L_j,R_j)\);
  • 考虑如何维护这些区间的变化;

  • 由于一次变化后,所有被影响到的区间一定是在左端点或者右端点;

  • 在每个区间的左端点记录\(Right\)集合,对右端点记录\(Left\)集合;

  • 对于在\(pos_i\)左边的区间的\(Left\),显然可以直接加到\(j\)\(Right\)上;

  • 右边区间的\(Right\)同理;

  • 对于左边区间的\(Right\),可以发现在变化过后所有的区间变成了相同的区间,并查集合并即可

  • 右边区间的\(Left\)和跨越\(pos_i\)的的区间同理;

  • 上述过程可以简单链表+并查集实现,查找包含的区间可以用\(set\)维护;

  • $Part  3 $

  • 考虑如何求最终的答案;

  • 先预处理一个正常情况下的答案;

  • 对于每个熊,设正常情况下最终位置为:\((x_i,y_i)\),相当于变成了\((x_i \pm 1, y_i \pm 1)\);

  • \((x_i+a)(y_i+b) - x_iy_i \ = \ ax_i + by_i + ab\);

  • 前缀和维护\(ax_i\)\(by_i\)再加个二维数点维护\(ab\)即可;

  • 。。。。。。

    #include<bits/stdc++.h>
    #define ll long long 
    #define mk make_pair
    #define fir first
    #define sec second 
    using namespace std;
    const int N=1000010;
    int n,px[N],py[N],idx[N],idy[N],dx[N],dy[N],p[N],rk[N];
    int fa[N],pre[N],nxt[N],nt[N],sz,sta[N],top;
    int Lx[N],Rx[N],Ly[N],Ry[N],Lx0[N],Rx0[N],Ly0[N],Ry0[N],tx[N],ty[N],rkx[N],rky[N];
    ll sumx[N],sumy[N],pre_ans;
    inline char gc(){
      static char*p1,*p2,s[1000000];
      if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
      return(p1==p2)?EOF:*p1++;
    }
    inline int rd(){
      int x=0;char c=gc();
      while(c<'0'||c>'9')c=gc();
      while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc();
      return x;
    }
    inline bool cmpx(const int&a,const int&b){return px[a]<px[b];}
    inline bool cmpy(const int&a,const int&b){return py[a]<py[b];}
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    int find_nxt(int x){return nxt[x]==x?x:nxt[x]=find_nxt(nxt[x]);}
    int find_pre(int x){return pre[x]==x?x:pre[x]=find_pre(pre[x]);}
    struct table{int hd,tl;};
    void link(table&A,int x){
      if(!A.hd){nt[A.hd=A.tl=x]=0;return;}
      nt[A.tl]=x;nt[A.tl=x]=0;
    }
    void link(table&A,table B){
      if(!B.hd)return;
      if(!A.hd){A=B;return;}
      nt[A.tl]=B.hd;A.tl=B.tl;
    }
    struct inter{
      int l,r;
      table L,R;
      void init(const int&id,const int&tl,const int&tr){
          L.hd=R.hd=L.tl=R.tl=0; 
          l=tl,r=p[id]=tr;
    //        link(L,id);
      }
    }A[N];
    typedef pair<int,int>pii;
    set<pii>S; set<pii>::iterator st,ed,I;
    void solve(const int*id,int*d,int*L,int*R,int*L0,int*R0){
      d[0]=d[n]=n+1;
      for(int i=0;i<=n;++i)pre[i]=nxt[i]=fa[i]=i;
      for(int i=1;i<n;++i)if(!d[i])pre[i]=i-1,nxt[i]=i+1;
      for(int i=1;i<=n;++i){
          int pos=id[i]-1,l=find_pre(pos),r=find_nxt(pos+1),f;
          L0[i]=l;R0[i]=r;
          d[l]--;if(!d[l])pre[l]=l-1,nxt[l]=l+1;
          d[r]--;if(!d[r])pre[r]=r-1,nxt[r]=r+1;
          A[i].init(i,l,r);
          st=S.lower_bound(mk(l,0));
          ed=S.lower_bound(mk(r,0));
          inter&tmp=A[i];
          for(I=st;I!=ed;++I){
              inter now=A[(*I).sec];
              if(now.r<=pos){
                  if(now.L.hd){
                      p[f=now.L.hd]=now.l;
                      for(int j=nt[f];j;j=nt[j])fa[find(j)]=find(f);
                      link(tmp.L,f);
                  }
                  link(tmp.L,now.R); 
              }else if(now.l>pos){
                  link(tmp.R,now.L);
                  if(now.R.hd){
                      p[f=now.R.hd]=now.r;
                      for(int j=nt[f];j;j=nt[j])fa[find(j)]=find(f);
                      link(tmp.R,f);
                  }
              }else{
                  int j=now.L.hd;
                  if(j&&p[j]<=pos){
                      p[f=j]=now.l;
                      for(j=nt[f];j&&p[j]<=pos;j=nt[j])fa[find(j)]=find(f);
                      link(tmp.L,f);
                  }
                  for(;j;j=nt[j])fa[find(j)]=find(i);
                  for(j=now.R.hd;j&&p[j]<=pos;j=nt[j])fa[find(j)]=find(i);
                  if(j){
                      p[f=j]=now.r;
                      for(j=nt[f];j;j=nt[j])fa[find(j)]=find(f);
                      link(tmp.R,f);
                  }
              }
          }
          link(tmp.L,i);
          S.erase(st,ed);
          S.insert(mk(l,i));
      }
      st=S.begin();ed=S.end();
      for(I=st;I!=ed;++I){
          inter tmp=A[(*I).sec];
          for(int j=tmp.L.hd;j;j=nt[j])L[j]=tmp.l,R[j]=p[j];
          for(int j=tmp.R.hd;j;j=nt[j])R[j]=tmp.r,L[j]=p[j];
      }
      S.erase(st,ed);
      for(int i=1;i<=n;++i)if(find(i)!=i)L[i]=L[fa[i]],R[i]=R[fa[i]];
    }
    struct tree{
      int sz,rt[N],ls[N*20],rs[N*20],sum[N*20];
      void insert(int&k,int lst,int l,int r,int x){
          sum[k=++sz]=sum[lst]+1;
          ls[k]=ls[lst],rs[k]=rs[lst];
          if(l==r)return;
          int mid=l+r>>1;
          if(x<=mid)insert(ls[k],ls[lst],l,mid,x);
          else insert(rs[k],rs[lst],mid+1,r,x);
      }
      int que(int k,int lst,int l,int r,int x,int y){
          if(l==x&&r==y)return sum[k]-sum[lst];
          int mid=l+r>>1;
          if(y<=mid)return que(ls[k],ls[lst],l,mid,x,y);
          else if(x>mid)return que(rs[k],rs[lst],mid+1,r,x,y);
          else return que(ls[k],ls[lst],l,mid,x,mid)+que(rs[k],rs[lst],mid+1,r,mid+1,y);
      }
      void insert(int x,int y){insert(rt[x],rt[x-1],1,n,y);}
      int query(int lx,int rx,int ly,int ry){
          if(lx>rx||ly>ry)return 0;
          return que(rt[rx],rt[lx-1],1,n,ly,ry);
      }
    }T;
    void pre_cal(){
      for(int i=0;i<=n;++i)dx[i]=dy[i]=0;
      for(int i=1;i<=n;++i)dx[Rx0[i]+1]--,dy[Lx0[i]]++;
      for(int i=1;i<=n;++i)dx[i]+=dx[i-1],dy[n-i+1]+=dy[n-i+2];
      for(int i=1;i<=n;++i)px[idx[i]]+=dx[i]+dy[i];
      for(int i=0;i<=n;++i)dx[i]=dy[i]=0;
      for(int i=1;i<=n;++i)dx[Ry0[i]+1]--,dy[Ly0[i]]++;
      for(int i=1;i<=n;++i)dx[i]+=dx[i-1],dy[n-i+1]+=dy[n-i+2];
      for(int i=1;i<=n;++i)py[idy[i]]+=dx[i]+dy[i];
      for(int i=1;i<=n;++i)pre_ans+=1ll*px[i]*py[i];
      for(int i=1;i<=n;++i)sumx[i]=px[idy[i]]+sumx[i-1],sumy[i]=py[idx[i]]+sumy[i-1];
    }
    ll cal(int id,int lx,int rx,int ly,int ry){
      ll tmp=pre_ans;
      tmp-=sumy[lx];
      tmp+=sumy[n]-sumy[rx];
      tmp-=sumx[ly];
      tmp+=sumx[n]-sumx[ry];
      tmp+=T.query(1,lx,1,ly);
      tmp-=T.query(1,lx,ry+1,n);
      tmp-=T.query(rx+1,n,1,ly);
      tmp+=T.query(rx+1,n,ry+1,n);
      int fgx=rkx[id]<=lx?-1:rkx[id]>rx?1:0;
      int fgy=rky[id]<=ly?-1:rky[id]>ry?1:0;
      tmp-=(ll)(px[id]+fgx)*(py[id]+fgy);
      tmp+=(ll)tx[id]*ty[id];
      return tmp;
    }
    int main(){
      freopen("bear.in","r",stdin);
      freopen("bear.out","w",stdout);
      n=rd();
      for(int i=1;i<=n;++i){
          tx[i]=px[i]=rd();
          ty[i]=py[i]=rd();
          idx[i]=idy[i]=fa[i]=i;
      }
      sort(idx+1,idx+n+1,cmpx);
      sort(idy+1,idy+n+1,cmpy);
      for(int i=1;i<=n;++i){
          rkx[idx[i]]=i;
          rky[idy[i]]=i;
          dx[i-1]=px[idx[i]]-px[idx[i-1]];
          dy[i-1]=py[idy[i]]-py[idy[i-1]]; 
      }
      for(int i=1;i<=n;++i)T.insert(i,rky[idx[i]]);
      memset(Lx,-1,sizeof(Lx));
      memset(Rx,-1,sizeof(Rx));
      memset(Ly,-1,sizeof(Ly));
      memset(Ry,-1,sizeof(Ry));
      solve(rkx,dx,Lx,Rx,Lx0,Rx0);
      solve(rky,dy,Ly,Ry,Ly0,Ry0);
      pre_cal();
      for(int i=1;i<=n;++i){
          ll ans = cal(i,Lx[i],Rx[i],Ly[i],Ry[i]);
          printf("%lld\n",ans);
      }
      return 0;
    }

转载于:https://www.cnblogs.com/Paul-Guderian/p/10617178.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值