[APIO2015]八鄰旁之橋
阿新 • • 發佈:2018-02-02
方法 type 都是 但是 有關 using 刪除 直接 tle )
把所有家和辦公室的坐標丟進一棵Splay中,平分
統計出左邊的sum和右邊的sum,左邊的sz和右邊的sz
題面在這裏
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]八鄰旁之橋