BZOJ4071: [Apio2015]巴邻旁之桥

138 篇文章 0 订阅
41 篇文章 0 订阅

在河同一边的可以不理他

问题就是有若干个人在A侧l位置,要通过桥到B侧r位置 l< r
当K=1时发现答案就是所有l,r的中位数
因为 ans=|pl|+|pr| (好有道理我怎么没发现qwq)
同时这启示我们(观察易得??qwq)当有两个桥时,每个人会选择更接近(l,r)中点的桥
所以我们将(l,r)按照中点排序后,两个桥将这些人分成了两部分,我们枚举这个分界线i,用主席树找中位数并统计其贡献

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;

const int maxn = 210000;

int kn,n,m;
int t[maxn<<1],tp,To[maxn<<1],K;
map<int,int>mt;
struct node{int l,r,mid;}a[maxn];
inline bool cmp(const node x,const node y){return x.mid<y.mid;}

struct segment{int num; ll sum;}seg[maxn<<2];
ll f[maxn],g[maxn];
void clear(const int x,const int l,const int r)
{
    seg[x].num=0,seg[x].sum=0;
    if(l==r) return;
    int mid=l+r>>1;
    clear(x<<1,l,mid); clear(x<<1|1,mid+1,r);
}
int loc;
void upd(const int x,const int l,const int r)
{
    seg[x].num++; seg[x].sum+=To[loc];
    if(l==r) return;
    int mid=l+r>>1;
    if(loc<=mid) upd(x<<1,l,mid);
    else upd(x<<1|1,mid+1,r);
}
int fd(const int x,const int l,const int r,int k)
{
    if(l==r) return l;
    int mid=l+r>>1,lc=x<<1;
    if(seg[lc].num<k) return fd(x<<1|1,mid+1,r,k-seg[lc].num);
    else return fd(x<<1,l,mid,k);
}
int q1(const int x,const int l,const int r)
{
    if(l>loc) return 0;
    if(r<=loc) return seg[x].num;
    int mid=l+r>>1;
    return q1(x<<1,l,mid)+q1(x<<1|1,mid+1,r);
}
ll q2(const int x,const int l,const int r)
{
    if(l>loc) return 0;
    if(r<=loc) return seg[x].sum;
    int mid=l+r>>1;
    return q2(x<<1,l,mid)+q2(x<<1|1,mid+1,r);
}

ll re,ans;

char s1[10],s2[10];

int main()
{
    scanf("%d%d",&kn,&n);
    for(int i=1;i<=n;i++)
    {
        int l,r;
        scanf("%s%d",s1,&l);
        scanf("%s%d",s2,&r);
        if(s1[0]==s2[0]) { ans+=abs(l-r);continue; }
        if(l>r) swap(l,r);
        a[++m]=(node){l,r,l+r};
        t[++tp]=l,t[++tp]=r;
    }
    sort(t+1,t+tp+1); t[0]=t[1]-1; K=0;
    for(int i=1;i<=tp;i++)
        if(t[i]!=t[i-1]) To[mt[t[i]]=++K]=t[i];
    for(int i=1;i<=m;i++) a[i].l=mt[a[i].l],a[i].r=mt[a[i].r];
    sort(a+1,a+m+1,cmp);

    ans+=m;
    if(!m) return printf("%lld\n",ans),0;
    ll temp=0;
    for(int i=1;i<=m;i++)
    {
        loc=a[i].l; upd(1,1,K);
        loc=a[i].r; upd(1,1,K);
        temp+=a[i].mid;
        loc=fd(1,1,K,i);
        ll c1=q1(1,1,K),c2=q2(1,1,K);
        f[i]=c1*To[loc]-c2+temp-c2-(i*2ll-c1)*To[loc];
    }
    if(kn==1) return printf("%lld\n",f[m]+ans),0;
    clear(1,1,K); temp=0;
    for(int i=m;i>=1;i--)
    {
        loc=a[i].l; upd(1,1,K);
        loc=a[i].r; upd(1,1,K);
        temp+=a[i].mid;
        loc=fd(1,1,K,(m-i+1));
        ll c1=q1(1,1,K),c2=q2(1,1,K);
        g[i]=c1*To[loc]-c2+temp-c2-((m-i+1)*2ll-c1)*To[loc];
    }
    re=LLONG_MAX;
    for(int i=1;i<=m;i++) re=min(re,f[i-1]+g[i]);
    printf("%lld\n",re+ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值