[线段树] Codechef December Challenge 2017. Red and blue points

没有大腿打CC是真的累

JOI这题一个套路

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int N=2010;

struct pt{
  int x,y,c;
  friend bool operator <(pt a,pt b){
    return a.x<b.x || (a.x==b.x && a.y<b.y);
  }
}p[N];

struct opt{
  int a,b,dy,dx;
  opt(){}
  opt(int x,int y,int z,int w):a(x),b(y),dx(z),dy(w){}
  friend bool operator <(opt a,opt b){
    if(a==b) return a.b<b.b;
    return 1LL*a.dy*b.dx<1LL*b.dy*a.dx;
  }
  friend bool operator ==(opt a,opt b){
    return 1LL*a.dy*b.dx==1LL*a.dx*b.dy;
  }
}s[N*N];

int t;
int n,m,b[N];

int mna[N<<2],mnb[N<<2],taga[N<<2],tagb[N<<2];

void build(int g,int l,int r){
  mna[g]=mnb[g]=taga[g]=tagb[g]=0;
  if(l==r) return ;
  int mid=l+r>>1;
  build(g<<1,l,mid); build(g<<1|1,mid+1,r);
}

inline void add(int g,int x,int y){
  mna[g]+=x-y; mnb[g]+=y-x;
  taga[g]+=x; tagb[g]+=y;
}

inline void Push(int g){
  if(!taga[g] && !tagb[g]) return ;
  add(g<<1,taga[g],tagb[g]); add(g<<1|1,taga[g],tagb[g]);
  taga[g]=tagb[g]=0;
}

inline void Up(int g){
  mna[g]=min(mna[g<<1],mna[g<<1|1]);
  mnb[g]=min(mnb[g<<1],mnb[g<<1|1]);
}

void Add(int g,int l,int r,int L,int R,int x,int y){
  if(l==L && R==r) return add(g,x,y);
  int mid=L+R>>1; Push(g);
  if(r<=mid) Add(g<<1,l,r,L,mid,x,y);
  else if(l>mid) Add(g<<1|1,l,r,mid+1,R,x,y);
  else Add(g<<1,l,mid,L,mid,x,y),Add(g<<1|1,mid+1,r,mid+1,R,x,y);
  Up(g);
}

int main(){
  scanf("%d",&t);
  while(t--){
    scanf("%d%d",&n,&m);
    int in=n,im=m;
    for(int i=1;i<=n;i++)
      scanf("%d%d",&p[i].x,&p[i].y),p[i].c=0;
    for(int i=n+1;i<=n+m;i++)
      scanf("%d%d",&p[i].x,&p[i].y),p[i].c=1;
    n+=m; sort(p+1,p+1+n); m=0;
    for(int i=1;i<=n;i++) b[i]=i;
    build(1,1,n);
    for(int i=1;i<=n;i++){
      if(p[i].c) Add(1,i,n,1,n,0,1);
      else Add(1,i,n,1,n,1,0);
    }
    for(int i=1;i<=n;i++)
      for(int j=i+1;j<=n;j++)
    s[++m]=opt(i,j,p[j].x-p[i].x,p[j].y-p[i].y);
    sort(s+1,s+1+m);
    int ans=min(im+mna[1],in+mnb[1]);
    /*int ans=1<<30,pa=0,pb=0;
    for(int i=1;i<=n;i++){
      if(p[i].c) pb++; else pa++;
      ans=min(ans,min(pa,pb)+min(in-pa,im-pb));
      }*/
    for(int i=1,j;i<=m;i=j){
      for(j=i;j<=m && s[i]==s[j];j++){
    if(p[b[s[j].a]].c!=p[b[s[j].b]].c){
      int l=b[s[j].a],r=b[s[j].b],pa=0,pb=0;
      if(l>r) swap(l,r);
      if(p[l].c==0) pa--; else pb--;
      if(p[r].c==0) pa++; else pb++;
      Add(1,l,r-1,1,n,pa,pb);
    }
    swap(p[b[s[j].a]],p[b[s[j].b]]),swap(b[s[j].a],b[s[j].b]);
      }
      ans=min(min(im+mna[1],in+mnb[1]),ans);
      /*int pa=0,pb=0;
      for(int k=1;k<=n;k++){
    if(p[k].c) pb++; else pa++;
    ans=min(ans,min(pa,pb)+min(in-pa,im-pb));
    }*/
    }

    printf("%d\n",ans);
  }
  return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值