1. 程式人生 > >[APIO2015]八鄰旁之橋

[APIO2015]八鄰旁之橋

方法 type 都是 但是 有關 using 刪除 直接 tle

題面在這裏

sol

這是一個\(Splay\)的題解
首先,如果一個人的家和辦公室在同一側,我們可以直接預處理;
如果不在同一側,也可以加上1(當然要過橋啦)

當k==1時

我們設橋的位置為\(pos\),每個人的家的位置為\(x[i]\),辦公室的位置為\(y[i]\),
則總代價為\(\sum_{i=1}^n (abs(x_i-pos)+abs(y_i-pos))\)
從這裏我們可以看到,其實家和辦公室的區別不是很明顯。
所以這個問題可以簡化為:
在數軸上任取一點a,最小化 \(\sum abs(a-x_i)\)
那麽我們將所有家和辦公室按照坐標排序,橋的位置肯定就在中間兩個端點的位置之間
至於怎麽統計相信大家都會吧(就在下面

)
把所有家和辦公室的坐標丟進一棵Splay中,平分
統計出左邊的sum和右邊的sum,左邊的sz和右邊的sz

當k==2時

再將上面的式子細分一下,我們能發現:
對於每條路徑\(x[i]->y[i]\),其實際長度和\(1/2(x[i]+y[i])\)距離橋的距離有關
於是我們可以考慮將所有路徑按照\((x[i]+y[i])\)排序
考慮建立兩顆Splay
首先把所有的節點全部插入一棵Splay中去
然後一對一對(註意此處)的從老Splay中丟到另一棵Splay中去,一邊統計答案
復雜度是O(nlogn)
應該是做完了

什麽!!!!你被卡常了!!!!

在下提交的時候,第二個點總是TLE...交了無數遍的95分
看著機房的其他dalao都是用線段樹做的,並不是很甘心啊......
但是方法總比困難多(hhhh)
具體可以參考我的代碼
(一遍過的dalao可以略過)

代碼

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define RG register
#define isr(i) (s[1][fa[(i)]]==(i))

using namespace std;
typedef long long ll;
const int N=200010;
const int inf=2147483647;
int cntt;
ll Ans[N];

struct
line{ int l,r; bool operator <(const line &a)const{ return (l+r)<(a.l+a.r); } }t[N]; inline int read() { RG int x=0,w=1;char ch=getchar(); while ((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar(); if (ch==‘-‘) w=0,ch=getchar(); while (ch>=‘0‘&&ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar(); return w?x:-x; } bool cmp(line a,line b){return (a.l+a.r)<(b.l+b.r);} struct Splay{ int root,tot,j,k; int s[2][N],fa[N],sz[N],cnt[N]; ll sum[N],v[N]; inline bool empty(){return !(bool)sz[root];} inline void clear(){ root=tot=0; memset(s,0,sizeof(s)); memset(fa,0,sizeof(fa)); memset(sz,0,sizeof(sz)); memset(sum,0,sizeof(sum)); memset(cnt,0,sizeof(cnt)); memset(v,0,sizeof(v)); } inline void init(int i,int x,int ff){ s[0][i]=s[1][i]=0;fa[i]=ff; v[i]=sum[i]=x;cnt[i]=sz[i]=1; } inline void update(int i){ sz[i]=sz[s[0][i]]+sz[s[1][i]]+cnt[i]; sum[i]=sum[s[0][i]]+sum[s[1][i]]+cnt[i]*v[i]; } inline void rot(int i){ j=fa[i];k=fa[j]; RG bool b=isr(i); fa[i]=k;s[isr(j)][k]=i; if(s[!b][i])fa[s[!b][i]]=j;s[b][j]=s[!b][i]; fa[j]=i;s[!b][i]=j; update(j); } inline void splay(int i,int a){ if(!a)root=i; while(fa[i]^a){ j=fa[i]; if(fa[j]^a) isr(i)^isr(j)?rot(i):rot(j); rot(i); } update(i); } inline void insert(int x){ RG int i=root,ff=0; while(v[i]!=x&&i){ ff=i;i=s[v[i]<x][i]; } if(i&&v[i]==x)cnt[i]++; else{ i=++tot; if(ff)s[v[ff]<x][ff]=i; init(i,x,ff); if(x==inf||x==-inf){ sz[i]=sum[i]=cnt[i]=0; } } splay(i,0); } inline int find(int x){ RG int i=root; while(v[i]!=x&&s[v[i]<x][i]) i=s[v[i]<x][i]; return i; } inline void Next(int x,int &lst,int &nxt){ RG int i=find(x);splay(i,0); if(v[i]>x)nxt=i; else { nxt=s[1][i]; while(s[0][nxt])nxt=s[0][nxt]; } if(v[i]<x)lst=i; else{ lst=s[0][i]; while(s[1][lst])lst=s[1][lst]; } } inline void Delete(int x){ RG int i=find(x); cnt[i]--;splay(i,0); } inline int kth(int k){ RG int i=root; while(1){ if(sz[s[0][i]]>=k)i=s[0][i]; else if(sz[s[0][i]]+cnt[i]>=k)return i; else k-=sz[s[0][i]]+cnt[i],i=s[1][i]; } } inline ll bridge(){//這裏是統計答案(不開long long可是會炸飛的) if(empty())return 0; int i=kth(sz[root]/2);splay(i,0); return 1ll*sz[s[0][i]]*v[i]-sum[s[0][i]]+sum[s[1][i]]-1ll*sz[s[1][i]]*v[i]; } }A,B; inline ll input(ll n){ RG char p[5],q[5]; RG ll ans=0;RG int s,T; cntt=0; A.insert(inf);A.insert(-inf); B.insert(inf);B.insert(-inf); for(RG int i=1;i<=n;i++){ scanf("%s",p+1);s=read(); scanf("%s",q+1);T=read(); if(p[1]!=q[1]){ ans++; t[++cntt].l=s;t[cntt].r=T; } else ans+=abs(s-T); } sort(t+1,t+cntt+1); for(RG int i=1;i<=cntt;i++){ A.insert(t[i].l);A.insert(t[i].r); Ans[i]=A.bridge(); /* 最關鍵的地方就是這裏 直接記錄一個Ans數組表示前i條路徑全部走到一座橋上的答案 之後就不用刪除了,把路徑倒著插入另外一棵線段樹中統計即可 */ } return ans; } inline void work2(int k,int n){ RG ll ans=input(n); RG ll minn=Ans[cntt]; if(A.empty()){ printf("%lld\n",ans); return; } for(RG int i=cntt;i>=1;i--){ B.insert(t[i].l);B.insert(t[i].r); minn=min(minn,Ans[i-1]+B.bridge()); //倒插統計部分 } printf("%lld\n",minn+ans); } inline void work1(int k,int n){ printf("%lld\n",input(n)+A.bridge()); } int main() { RG int k,n; k=read();n=read(); if(k^1)work2(k,n); else work1(k,n); return 0; }

[APIO2015]八鄰旁之橋