题目描述:
QAQ…
题目分析:
看上去看不可做…
K只有可能为 1 和 2
我们想一下1的情况怎么做
如果工作地点在一侧,那么路程就是 r-l
然后我们对于不在一侧的线段端点求个中位数 肯定能使总路程最小…
当K为2的时候
取每个线段的中点,如果靠近左边的桥,就往左边过桥,否则往右边过桥。
枚举一个分割点,然后两边都是k=1的情况,用线段树求
题目链接:
Ac 代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define int long long
using namespace std;
const int maxm=1600001;
int n,t,k;
int s[maxm<<2],c[maxm<<2];
int hash[maxm],ans[maxm];;
struct node{
int l,r;
}link[maxm];
inline bool comp(node x,node y){return x.l+x.r<y.l+y.r;}
void insert(int o,int l,int r,int ind,int v)
{
if(l>=r)
{
c[o]++,s[o]+=v;
return;
}
int mid=(l+r)>>1;
ind<=mid?insert((o<<1),l,mid,ind,v):insert((o<<1)|1,mid+1,r,ind,v);
s[o]=s[(o<<1)]+s[(o<<1)|1],c[o]=c[(o<<1)]+c[(o<<1)|1];
}
int ask(int o,int l,int r,int x)
{
if(l>=r) return hash[l]*x;
int mid=(l+r)>>1;
return c[(o<<1)]<x?s[(o<<1)]+ask((o<<1)|1,mid+1,r,x-c[(o<<1)]):ask((o<<1),l,mid,x);
}
inline int query(int x){return s[1]-2*ask(1,1,t,x);}
signed main()
{
//freopen("cstdiorank1AK.in","r",stdin);
//freopen("cstdiorank1AK.out","w",stdout);
int Ans=0,tot=0,num=0;
scanf("%lld%lld",&k,&n);
for(int i=1;i<=n;i++)
{
char s1[10],s2[10];
int l,r;
scanf("%s%lld%s%lld",s1,&l,s2,&r);
if(l>r) std::swap(l,r);
if(s1[0]==s2[0]) Ans+=r-l;
else
{
Ans++;
link[++tot].l=l,link[tot].r=r;
hash[++num]=l,hash[++num]=r;
}
}
n=tot;
sort(hash+1,hash+num+1);
sort(link+1,link+n+1,comp);
t=unique(hash+1,hash+num+1)-hash-1;
for(int i=1;i<=n;i++)
{
link[i].l=lower_bound(hash+1,hash+t+1,link[i].l)-hash;
link[i].r=lower_bound(hash+1,hash+t+1,link[i].r)-hash;
}
for(int i=1;i<=n;i++)
{
insert(1,1,t,link[i].l,hash[link[i].l]);
insert(1,1,t,link[i].r,hash[link[i].r]);
ans[i]=query(i);
}
if(k==1) printf("%lld\n",ans[n]+Ans);
else
{
memset(s,0,sizeof(s));
memset(c,0,sizeof(c));
int minx=ans[n];
for(int i=n;i>=1;i--)
{
insert(1,1,t,link[i].l,hash[link[i].l]);
insert(1,1,t,link[i].r,hash[link[i].r]);
minx=std::min(minx,ans[i-1]+query(n-i+1));
}
printf("%lld\n",minx+Ans);
}
return 0;
}