bzoj3170 [Tjoi2013]松鼠聚会(转换坐标系)

分类讨论树套树了一波 nlog2n 大常数成功TLE。qaq
正解是转换坐标系。我们发现求得其实就是切比雪夫距离之和,也就是 j=1nmax(|xixj|,|yiyj|) 。而我们可以通过变换坐标系,(x,y)->(x+y,x-y),也即把坐标轴旋转45度,我们发现此时两点间的曼哈顿距离正好是原来切比雪夫距离的二倍。而曼和顿距离求和就比较好求了,我们分别求x坐标的贡献 和 y坐标的贡献,排个序即可。

树套树

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,aa[N],m,rt[N],fa[N*17],c[N*17][2],sz[N*17],v[N*17],owo=0,vx[N*17],vy[N*17];
ll sumx[N*17],sumy[N*17],ans[N],Ans=1LL<<60,V,X,Y;
struct point{
    int x,y,id;
    friend bool operator<(point a,point b){return a.x<b.x;}
}a[N];
inline void update(int p){
    int l=c[p][0],r=c[p][1];
    sz[p]=sz[l]+sz[r]+1;
    sumx[p]=sumx[l]+sumx[r]+vx[p];
    sumy[p]=sumy[l]+sumy[r]+vy[p];
}
inline void rotate(int x,int &k){
    int y=fa[x],z=fa[y],l=x==c[y][1],r=l^1;
    if(y==k) k=x;
    else c[z][y==c[z][1]]=x;
    fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
    c[y][l]=c[x][r];c[x][r]=y;update(y);update(x);
}
inline void splay(int x,int &k){
    while(x!=k){
        int y=fa[x],z=fa[y];
        if(y!=k){
            if(x==c[y][1]^y==c[z][1]) rotate(x,k);
            else rotate(y,k);
        }rotate(x,k);
    }
}
inline void ins(int &p,int Fa){
    if(!p){p=++owo;fa[p]=Fa;c[p][0]=c[p][1]=0;v[p]=V;vx[p]=X;vy[p]=Y;sz[p]=1;update(p);return;}
    if(V<=v[p]) ins(c[p][0],p);
    else ins(c[p][1],p);
}
inline void add(int x){for(;x<=m;x+=x&-x) ins(rt[x],0),splay(owo,rt[x]);}
inline int qsum1(int p){//<=V
    if(!p) return 0;
    if(v[p]>V) return qsum1(c[p][0]);
    X+=vx[p]+sumx[c[p][0]];Y+=vy[p]+sumy[c[p][0]];
    return sz[c[p][0]]+1+qsum1(c[p][1]); 
}
inline int qsum2(int p){//>=V
    if(!p) return 0;
    if(v[p]<V) return qsum2(c[p][1]);
    X+=vx[p]+sumx[c[p][1]];Y+=vy[p]+sumy[c[p][1]];
    return sz[c[p][1]]+1+qsum2(c[p][0]);
}
inline ll ask1(int x,ll a,ll b){
    ll res=0;
    for(;x;x-=x&-x){X=0,Y=0;int cnt=qsum1(rt[x]);res+=a*cnt-X+b*(sz[rt[x]]-cnt)-(sumy[rt[x]]-Y);}
    return res;
}
inline ll ask2(int x,ll a,ll b){
    ll res=0;
    for(;x;x-=x&-x){X=0,Y=0;int cnt=qsum1(rt[x]);res+=a*cnt-X-b*(sz[rt[x]]-cnt)+(sumy[rt[x]]-Y);}
    return res;
}
inline ll ask3(int x,ll a,ll b){
    ll res=0;
    for(;x;x-=x&-x){X=0,Y=0;int cnt=qsum2(rt[x]);res+=-a*cnt+X-b*(sz[rt[x]]-cnt)+(sumy[rt[x]]-Y);}
    return res;
}
inline ll ask4(int x,ll a,ll b){
    ll res=0;
    for(;x;x-=x&-x){X=0,Y=0;int cnt=qsum2(rt[x]);res+=-a*cnt+X+b*(sz[rt[x]]-cnt)-(sumy[rt[x]]-Y);}
    return res;
}
int main(){
//  freopen("meeting.in","r",stdin);
//  freopen("meeting.out","w",stdout);
    n=read();
    for(int i=1;i<=n;++i) a[i].x=read(),a[i].y=aa[i]=read();
    sort(aa+1,aa+n+1);m=unique(aa+1,aa+n+1)-aa-1;
    sort(a+1,a+n+1);
    for(int i=1;i<=n;++i) a[i].id=lower_bound(aa+1,aa+m+1,a[i].y)-aa;
    for(int i=1;i<=n;++i){//<=a,<=b
        int now=i;while(now<=n&&a[now].x<=a[i].x) X=a[now].x,Y=a[now].y,V=a[now].x-a[now].y,add(a[now].id),++now;
        --now;for(int j=i;j<=now;++j) V=a[j].x-a[j].y,ans[j]+=ask1(a[j].id,a[j].x,a[j].y);i=now;
    }owo=0;memset(rt,0,sizeof(rt));
    for(int i=1;i<=n;++i){//<=a,>b
        int now=i;while(now<=n&&a[now].x<=a[i].x) 
        X=a[now].x,Y=a[now].y,V=a[now].x+a[now].y,add(m-a[now].id+1),++now;
        --now;for(int j=i;j<=now;++j) V=a[j].x+a[j].y,ans[j]+=ask2(m-a[j].id,a[j].x,a[j].y);i=now;
    }owo=0;memset(rt,0,sizeof(rt));
    for(int i=n;i>=1;--i){//>a,<b
        int now=i;while(now&&a[now].x==a[i].x) V=a[now].x-a[now].y,ans[now]+=ask3(m-a[now].id,a[now].x,a[now].y),--now;
        ++now;for(int j=i;j>=now;--j) X=a[j].x,Y=a[j].y,V=a[j].x-a[j].y,add(m-a[j].id+1);i=now;
    }owo=0;memset(rt,0,sizeof(rt));
    for(int i=n;i>=1;--i){//>a,<=b
        int now=i;while(now&&a[now].x==a[i].x) V=a[now].x+a[now].y,ans[now]+=ask4(a[now].id,a[now].x,a[now].y),--now;
        ++now;for(int j=i;j>=now;--j) X=a[j].x,Y=a[j].y,V=a[j].x+a[j].y,add(a[j].id);i=now;
    }for(int i=1;i<=n;++i) Ans=min(Ans,ans[i]);
    printf("%lld\n",Ans);
    return 0;
}

转换坐标系

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n;
ll ans[N],sumx=0,sumy=0;
struct point{
    int x,y,id;
}a[N];
inline bool cmp1(point a,point b){return a.x<b.x;}
inline bool cmp2(point a,point b){return a.y<b.y;}
int main(){
//  freopen("a.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i){
        int x=read(),y=read();a[i].id=i;a[i].x=x+y;a[i].y=x-y;
        sumx+=a[i].x;sumy+=a[i].y;
    }sort(a+1,a+n+1,cmp1);ll sum=0;
    for(int i=1;i<=n;++i) sum+=a[i].x,ans[a[i].id]+=(ll)i*a[i].x-sum+sumx-sum-(ll)(n-i)*a[i].x;
    sum=0;sort(a+1,a+n+1,cmp2);
    for(int i=1;i<=n;++i) sum+=a[i].y,ans[a[i].id]+=(ll)i*a[i].y-sum+sumy-sum-(ll)(n-i)*a[i].y;
    ll Ans=1LL<<60;for(int i=1;i<=n;++i) Ans=min(Ans,ans[i]);
    printf("%lld\n",Ans>>1);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值