codeforces802 A-O Helvetic Coding Contest 2017 online mirror
水题 同B
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int maxn=1000000;
int n,k,a[maxn],num;
bool ex[maxn],need[maxn];
int main()
{//freopen("t.txt","r",stdin);
scanf("%d%d",&n,&k);
num=0;
memset(ex,0,sizeof(ex));
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
int ans=0;
for(int i=0;i<n;i++)
{
if(ex[a[i]]) continue;
if(num<k)
{
num++;
ans++;
ex[a[i]]=true;
}
else
{
int sum=0;
memset(need,0,sizeof(need));
for(int j=i+1;j<n&&sum<k-1;j++)
{
if(!ex[a[j]])continue;
if(need[a[j]]==false)sum++;
need[a[j]]=true;
}
for(int j=1;j<=n;j++)
{
if(ex[j]&&(!need[j])){ex[j]=false;ex[a[i]]=true;ans++;break;}
}
}
}
printf("%d\n",ans);
return 0;
}
经典的内存管理OPT算法
不过基本没有实际应用价值,因为操作系统不可能知道之后要调用哪些内存。
用map或者堆都可以实现 堆会快很多
map版本
#include <bits/stdc++.h>
using namespace std;
set<int> s;
int n,cs,k,c[400400],ne[400400],la[400400],cc;
int main(){
scanf("%d %d",&n,&k);
for(int i=0;i<n;i++)scanf("%d",&c[i]);
for(int i=n-1;i>=0;i--){
if(!la[c[i]])ne[i]=1e6,la[c[i]]=i;
else ne[i]=la[c[i]],la[c[i]]=i;
}
for(int i=0;i<n;i++){
if(s.count(i)){s.erase(i),s.insert(ne[i]);continue;}
if((int)s.size()<k)
s.insert(ne[i]),cc++;
else
s.erase(--s.end()),s.insert(ne[i]),cc++;
}
printf("%d\n",cc);
}
优先队列版本
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
#include<deque>
#include<queue>
using namespace std;
const int maxn=500000;
int n,k,a[maxn],num,ne[maxn],ls[maxn];
bool ex[maxn];
priority_queue<int>que;
int main()
{//freopen("t.txt","r",stdin);
while(!que.empty())que.pop();
scanf("%d%d",&n,&k);
num=0;
memset(ex,0,sizeof(ex));
for(int i=0;i<n;i++)scanf("%d",&a[i]);
for(int i=n-1;i>=0;i--)
{
if(!ls[a[i]])ls[a[i]]=1e+8;
ne[i]=ls[a[i]];
ls[a[i]]=i;
}
int ans=0;
int j=1;
int maxv=0,max2v=0;
for(int i=0;i<n;i++)
{
if(ex[i])
{
while(que.size()>0&&que.top()<=i)que.pop();
ex[i]=false;
if(ne[i]<=n)ex[ne[i]]=true;
que.push(ne[i]);
continue;
}
if(num<k)
{
num++;
ans++;
if(ne[i]<=n)ex[ne[i]]=true;
que.push(ne[i]);
}
else
{
int nowv=que.top();
que.pop();
ans++;
if(nowv<=n)ex[nowv]=false;
if(ne[i]<=n)ex[ne[i]]=true;
que.push(ne[i]);
}
}
printf("%d\n",ans);
return 0;
}
费用流
考虑最暴力的方法,每次调用新的书都直接购买,这多半不是最优解。
有没有办法优化到最优解呢?
考虑对于书架上的每一个位置,让它在恰当的时候继续持有书,在恰当的时候购买新的书,这样我们就能找到最优解了。
对于相同的书,连一条费用为-c[]的边(持有即相当于不用买新的所以答案-c[]),不同的书连一条费用为0的边.
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define N 200020
const LL INF = 1e9;
int nxt[N], cost[N], cap[N], to[N], head[N], cnt;
void init(){
memset(head, -1, sizeof head);
}
void add_Edge(int S, int T, int c, int w){
nxt[cnt] = head[S], to[cnt] = T, cap[cnt] = c, cost[cnt] = w, head[S] = cnt ++;
nxt[cnt] = head[T], to[cnt] = S, cap[cnt] = 0, cost[cnt] = -w, head[T] = cnt ++;
}
int prv[N], vis[N];
LL dist[N];
LL SPFA(int S, int T, int vet){
queue <int> Q;
fill(dist, dist + vet, INF);
fill(prv, prv + vet, -1);
dist[S] = 0, Q.push(S), vis[S] = true;
while(!Q.empty() ){
int x = Q.front();
Q.pop(), vis[x] = false;
for(int id = head[x]; ~id; id = nxt[id]) if( cap[id] ){
int y = to[id];
if(dist[y] > dist[x] + cost[id]){
dist[y] = dist[x] + cost[id];
prv[y] = id;
if(!vis[y]) Q.push(y), vis[y] = true;
}
}
}
if(!~prv[T]){ return INF; }
int cur = T;
while( cur != S ) {
cur = prv[cur];
cap[cur] --;
cap[cur xor 1] ++;
cur = to[cur xor 1];
}
return dist[T];
}
int a[N], c[N], n, m;
int main(){
//freopen("t.txt", "r", stdin);
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", a + i);
for(int i = 1; i <= n; i ++) scanf("%d", c + i);
init();
LL ans = 0;
int S = n + 1, T = 2 * n + 2;
for(int i = 1; i <= n; i ++){
ans += c[a[i]];
add_Edge(S, i, 1, 0);
add_Edge(i, S + i, 1, -INF);
for(int j = i + 1; j <= n; j ++){
if(a[i] == a[j]) add_Edge(i + S, j, 1, -c[a[j]]);
else add_Edge(i + S, j, 1, 0);
}
add_Edge(i + S, T, 1, 0);
}
for(int step = 1; step <= m; step ++){
LL tmp = SPFA(S, T, T + 1);
if(tmp >= 0) break;
ans += tmp;
}
cout << ans + INF * n << endl;
}
根据泊松分布的特点,对称轴两边的概率密度最大。
用这个特点来判断是泊松分布还是平均分布。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int a[250],b[250];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
for(int i=0;i<250;i++)scanf("%d",&a[i]);
//for(int i=0;i<250;i++)scanf("%d",&b[i]);
int mina=a[0],maxa=a[0],minb=b[0],maxb=b[0];
double mida=0;
for(int i=0;i<250;i++)
{
mida+=a[i];
//minb=min(minb,b[i]);maxb=max(maxb,b[i]);
}
mida/=250.;
int sum=0,sumb=0;
double len=mida/2;
for(int i=0;i<250;i++)
{
if(a[i]>(mida-len)&&a[i]<(mida+len))sum++;
if(a[i]<=1)sumb++;
}
if(sum<180||sumb>3)printf("uniform\n");
else printf("poisson\n");
}
return 0;
}
在D的基础上,首先判断是 泊松分布还是平均分布
如果是泊松分布求所有值的平均值,否则求最大值和最小值的平均值。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int a[250],b[250];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
for(int i=0;i<250;i++)scanf("%d",&a[i]);
//for(int i=0;i<250;i++)scanf("%d",&b[i]);
double mina=a[0],maxa=a[0],minb=b[0],maxb=b[0];
double mida=0;
for(int i=0;i<250;i++)
{
mida+=a[i];
mina=min(mina,(double)a[i]);maxa=max(maxa,(double)a[i]);
}
mida/=250.;
int sum=0,sumb=0;
double len=mida/2;
for(int i=0;i<250;i++)
{
if(a[i]>(mida-len)&&a[i]<(mida+len))sum++;
if(a[i]<=1)sumb++;
}
if(sum<180||sumb>3)//printf("uniform\n");
{
printf("%.0lf\n",(mina+maxa)/2+0.5);
}
else //printf("poisson\n");
{
printf("%.0lf\n",mida+0.5);
}
}
return 0;
}
由于出现了负数,所以D中简单粗暴的方法不可取了。
不过数据并没有变复杂,由于平均分布相对于泊松分布更加离散,所以方差会有明显的区别。
利用方差来判断,就不怕负数了。
#include<bits/stdc++.h>
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=a;i>=b;i--)
using namespace std;
inline int read() {
int x=0,f=1; char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1; ch=getchar();}
while (ch>='0'&&ch<='9') {x=x*10+(ch^48); ch=getchar();}
return x*f;
}
const int N = 251;
const int P = 1005;
const double e = 2.718281828459045235360287471352;
int a[N];
double tp1[P<<1],tp2[P<<1];
int main() {
int T=read();
while (T--) {
int mx=0;
double mean=0; rep(i,1,250) a[i]=read(),mean+=a[i],mx=max(mx,a[i]);
double D=0; mean/=250; rep(i,1,250) D+=a[i]*a[i];
D/=(double)250; D-=mean*mean;
double sigma = sqrt(D);
if (mx/sigma<=1.9) puts("uniform"); else puts("poisson");
}
return 0;
}
公共子序列问题 O(N^2)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<string>
#include<vector>
#include<cstring>
using namespace std;
int dp[1000][1000];
int LCS(int n1,int n2,string s1,string s2)
{
for(int i=0;i<n1;i++)
for(int j=0;j<n2;j++)
{
if(i>0)dp[i][j]=dp[i-1][j];
if(j>0&&dp[i][j-1]>dp[i][j])dp[i][j]=dp[i][j-1];
if(s1[i]==s2[j])
{
if(i==0||j==0)dp[i][j]=1;
else dp[i][j]=dp[i-1][j-1]+1;
}
}
return dp[n1-1][n2-1];
}
int main()
{//freopen("t.txt","r",stdin);
ios::sync_with_stdio(false);
string s1,s2;
s2="heidi";
while(cin>>s1)
{
memset(dp,0,sizeof(dp));
int len=LCS(s1.length(),s2.length(),s1,s2);
if(len==5)printf("YES\n");
else printf("NO\n");
s1.clear();s2.clear();
}
return 0;
}
很简单的一道计数题 直接看代码吧~
#include<cstdio>
#include<algorithm>
using namespace std;
int Comb[110][6], C[110], S;
int main(){
int i, j;
for(i=0;i<=45;i++){
Comb[i][0]=1;
for(j=1;j<=5&&j<=i;j++)Comb[i][j]=Comb[i-1][j]+Comb[i-1][j-1];
}
scanf("%d",&S);
for(i=45;i>=5;i--){
while(S>=Comb[i][5]){
C[i]++;
S-=Comb[i][5];
}
}
for(i=0;i<45;i++){
while(C[i]--)printf("b");
printf("a");
}
printf(" aaaaab\n");
}
后缀数组+记忆化搜索
很有趣的一道后缀数组题
题目的意思就是让我们求一个字符串L所有子串在这个串中出现的次数。
这种问题用脚指头想都知道肯定要上后缀数组啦!
对于数组height[l...r] 其中的最小值就是他们的公共前缀的长度,而这个公共前缀就是L的一个唯一的子串。r-l+1就是它出现的次数。(ps:不懂height数组的自行学习后缀数组再来看)
怎样高效统计呢?
对于height[0,len(L)-1]我们能否高效的找到它的最小值?可以。
那么假设最小值的位置是mid 然后我们把height分成l..mid mid+1....r分别计算,那么怎么合并呢?
两个子片段的公共最小Height值就是他们的公共前缀长度,我们可以知道这个公共前缀出现的次数,但是同时要排除他们在字串中贡献的值。
太难描述了,具体转移方法看代码吧。
注意,mid一定是最小值所在的位置,不可以随意划分height数组,那样是错的。
求mid不能太暴力,会TLE。
#include <iostream>
#include <cstring>
#include <cstdio>
#include<vector>
using namespace std;
const int MAX = 100500;
const int nMAX = 105;
const int mMAX = 1005;
int strnum;
char str[MAX];
int source[MAX];
int sa[MAX], rk[MAX], height[MAX];
int wa[MAX], wb[MAX], wv[MAX], wd[MAX];
bool vis[nMAX];
int id[MAX];
int anslen, anspos[mMAX], ansnum;
const int MAXN=200000+100;
void radix(int *str,int *a,int *b,int n,int m)
{
static int count[MAXN];
memset(count,0,sizeof(count));
for(int i=0;i<n;++i)++count[str[a[i]]];
for(int i=1;i<=m;++i)count[i]+=count[i-1];
for(int i=n-1;i>=0;--i)b[--count[str[a[i]]]]=a[i];
}
void sorted_suffix_array(int *str,int *sa,int n,int m)
{
static int rank[MAXN],a[MAXN],b[MAXN];
for(int i=0;i<n;++i)rank[i]=i;
radix(str,rank,sa,n,m);
rank[sa[0]]=0;
for(int i=1;i<n;++i)rank[sa[i]]=rank[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
for(int i=0;(1<<i) <n;++i)
{
for(int j=0;j<n;++j)
{
a[j]=rank[j]+1;
b[j]=j+(1<<i)>=n? 0:rank[j+(1<<i)]+1;
sa[j]=j;
}
radix(b,sa,rank,n,n);
radix(a,rank,sa,n,n);
rank[sa[0]]=0;
for(int j=1;j<n;++j)
{
rank[sa[j]]=rank[sa[j-1]]+(a[sa[j-1]]!=a[sa[j]]||b[sa[j-1]]!=b[sa[j]]);
}
}
}
void calc_height(int *str,int *sa,int *h,int n)
{
static int Rank[MAXN];
int k=0;
h[0]=0;
for(int i=0;i<n;++i)Rank[sa[i]]=i;
for(int i=0;i<n;++i)
{
k= k==0?0:k-1;
if(Rank[i]!=0)
while(str[i+k]==str[sa[Rank[i]-1]+k])++k;
h[Rank[i]]=k;
}
}
int stlen;
long long dp(long long l,long long r,long long &summ,vector<int>&nemi,int flag)
{
if(l==r){summ=(long long)stlen-(long long)sa[l];return summ;}
int mid=-1;
vector<int>nemr;
if(flag!=-1)mid=nemi[flag]-1;
else
{
nemi.push_back(l+1);
for(int i=l+1;i<r;i++)
{
if(height[i+1]<height[nemi[(int)nemi.size()-1]])nemi.push_back(i+1);
}
flag=(int)nemi.size()-1;
mid= nemi[flag]-1;
}
long long sum1,sum2;
long long int minh=min(min(dp(l,mid,sum1,nemi,flag-1),dp(mid+1,r,sum2,nemr,-1)),(long long)height[mid+1]);
summ=sum1+sum2+minh*(r-l+1)*(r-l+1)-minh*(mid-l+1)*(mid-l+1)-minh*(r-mid)*(r-mid);
nemr.clear();
return minh;
}
long long int solve(char *st)
{
stlen=strlen(st);
for(int i=0;i<stlen;i++)
source[i]=st[i]-'a'+1;
sorted_suffix_array(source,sa,(int)stlen,126);
calc_height(source,sa,height,(int)stlen);
height[0]=1e+8;
long long int ans=0;
vector<int>mi;
mi.clear();
dp(0,stlen-1,ans,mi,-1);
return ans;
}
int main()
{//freopen("t.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",&str);
printf("%I64d\n",solve(str));
}
return 0;
}
J Send the Fool Further! (easy)
水题
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<string>
#include<vector>
#include<cstring>
using namespace std;
vector<int>adj[200];
int w[200][200];
int dist[200];
void dfs(int cur,int fa,int len)
{
for(int i=0;i<adj[cur].size();i++)
{
int ne=adj[cur][i];
if(ne==fa)continue;
dist[ne]=min(dist[ne],len+w[cur][ne]);
dfs(ne,cur,len+w[cur][ne]);
}
}
int main()
{//freopen("t.txt","r",stdin);
int n;
scanf("%d",&n);
int u,v;
for(int i=0;i<n-1;i++)
{
scanf("%d%d",&u,&v);
scanf("%d",&w[u][v]);
w[v][u]=w[u][v];
adj[u].push_back(v);
adj[v].push_back(u);
}
for(int i=1;i<n;i++)
dist[i]=99999999;
dist[0]=0;
dfs(0,-1,0);
int ans=0;
for(int i=0;i<n;i++)
ans=max(ans,dist[i]);
printf("%d\n",ans);
return 0;
}
K Send the Fool Further! (medium)
比较有趣的树形DP
考虑最优解,必有两种情况,要么在0结束,要么不在0结束。对于其他节点也是一个道理。
所以设dp[i][bool]为从i出发,的最优解,bool为0则最后回到i否则不用回到i
转移方程直接看程序吧 很简单。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200000;
int dp[maxn][2];
vector< pair<int,int> > E[maxn];
int n,k;
bool cmp(const pair<int,int> &a, const pair<int,int> &b){
return (a.first > b.first);
}
void dfs(int u, int p){
vector< pair<int,int> > c;
set<int> st;
for(auto e: E[u]){
int v = e.first;
int cst = e.second;
if(v == p) continue;
dfs(v,u);
c.push_back({dp[v][0] + cst,v});
}
sort(c.begin(), c.end(), cmp);
int tk = min(k-1, (int)(c.size()));
for(int i = 0; i < tk; i++){
dp[u][0] += c[i].first;
st.insert(c[i].second);
}
int extra = 0;
if(tk != (int)(c.size()))
extra = c[tk].first;
for(auto e: E[u]){
int v = e.first;
int cst = e.second;
if(v == p) continue;
if(st.count(v) == 0)
dp[u][1] = max(dp[u][1], dp[v][1] + dp[u][0] + cst);
else
dp[u][1] = max(dp[u][1], dp[u][0] - dp[v][0] + extra + dp[v][1]);
}
st.clear();
c.clear();
}
int main(){
scanf("%d%d", &n, &k);
for(int i = 1; i < n; i++){
int u,v,c;
scanf("%d%d%d", &u, &v, &c);
E[u].push_back({v,c});
E[v].push_back({u,c});
}
dfs(0,-1);
cout << max(dp[0][0], dp[0][1]) << "\n";
return 0;
}
L Send the Fool Further! (hard)
这道题题意说的不是很清楚 总的来说是让我们求E(0)
E(0)和E(v)有线性关系,v是0的孩子。
所以,暴力解方程组的方法是可以求出来的。但是复杂度太高O(n^3)
有没有聪明一点的方法呢?
还是考虑E(0)和E(v)的关系
E(v)由它的孩子和E(0)线性组合而成。假设E(v)不考虑0的情况下期望为G(v)G(v)可以在dfs()的过程中求出来。
我们通过一定的代数变形可以直接由G(v)推出E(v)
嗯。。大致就是这样。。还是那句话 近世代数太重要了。
代码很清晰
直观的看的话 在树上求解问题,最关键的就是找出递归关系,
也就是对于当前节点求解(做到)不需考虑它的父亲部分(这样我们离答案就很近了因为最后的答案G【0】就是不需要考虑父亲的)。
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <string>
#define SIZE 100005
#define MOD 1000000007
using namespace std;
typedef long long int ll;
struct edge
{
int to,cost;
edge(int to=0,int cost=0):to(to),cost(cost){}
};
vector <edge> vec[SIZE];
ll F[SIZE],G[SIZE];
int nd[SIZE];
int n;
ll mpow(ll m,ll t)
{
if(t==0) return 1LL;
ll ret=mpow(m*m%MOD,t/2);
if(t%2==1) ret=ret*m%MOD;
return ret;
}
ll inv(ll m)
{
return mpow(m,MOD-2);
}
void dfs(int v=0,int p=-1)
{
if(vec[v].size()==1)
{
F[v]=G[v]=0;
return;
}
ll sumG=0,sumF=vec[v].size();
for(int i=0;i<vec[v].size();i++)
{
edge e=vec[v][i];
sumG+=e.cost;
if(sumG>=MOD) sumG-=MOD;
if(e.to!=p)
{
dfs(e.to,v);
sumG+=G[e.to];
if(sumG>=MOD) sumG-=MOD;
sumF-=F[e.to];
if(sumF<0) sumF+=MOD;
}
}
ll g=inv(sumF);
F[v]=g;
G[v]=g*sumG%MOD;
}
int main()
{ freopen("t.txt","r",stdin);
scanf("%d",&n);
for(int i=0;i<n-1;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
vec[a].push_back(edge(b,c));
vec[b].push_back(edge(a,c));
}
dfs();
printf("%lld\n",G[0]);
return 0;
}
每个序列排序后前k个数的和
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int num[3000];
int main()
{//freopen("t.txt","r",stdin);
int n,m,t;
int mins=-1;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d",&num[i]);
}
sort(num,num+n);
int ans=0;
for(int i=0;i<m;i++)
ans+=num[i];
printf("%d\n",ans);
return 0;
}
N April Fools' Problem (medium)
很有趣的一道贪心题。
给定两个正整数序列A和B
求两个A和B的长度为k的子序列 a和b满足 a中的第i个元素在A中的位置<= b中的第i个元素在B中的位置 并且和最小。
用贪心的方法,先求一个最小的合法解 a[i]和b[j]且 j>=i,然后他们之间[i...j]就可以放反向的数对了也就是在找最小解的时候可以允许j<i了 。
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL ;
const int maxn=3000;
LL a[maxn],b[maxn],va[maxn],vb[maxn],verse[maxn];
int n,k;
int main()
{//freopen("t.txt","r",stdin);
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)scanf("%I64d",&a[i]);
for(int i=0;i<n;i++)scanf("%I64d",&b[i]);
LL ans=0;
b[n]=1e+18;
while(k--)
{int ra=-1,rb;
for(int i=n-1,j=n,minb=n;i>=0;i--)
{
if(j>i)j=i;
if(b[j]<b[minb]&&!vb[j])minb=j;
while((j-1)>=0&&verse[j-1])if(b[--j]<b[minb]&&!vb[j])minb=j;
if(!va[i]&&(ra==-1||a[i]+b[minb]<a[ra]+b[rb]))ra=i,rb=minb;
}
ans+=a[ra]+b[rb];va[ra]++;vb[rb]++;
for(int i=ra;i<rb;i++)verse[i]++;
for(int i=rb;i<ra;i++)verse[i]--;
}
printf("%I64d\n",ans);
return 0;
}
题意和N一样 数据提高到了500000 非常有趣的一道优化题
显然N中我们O(n^2)的算法要TLE
有什么办法优化到O(nlogn)么?
1.首先考虑当前已经选定了N个题目以及他们合法的打印时间,对于以后可选的打印时间,可以用来优化答案。O(n)
2.那么主要问题就变成了如何选取恰当的N个初始题目。
我们用二分的方法选取一个阀值mid 比它更优的 我们才让它成为备选题目,这样不断二分mid 总会找到一个最恰当的mid使得选中的题目正好是m个最优的。
当然如果出现比m多的情况说明他们是相等的题目和打印时间。
看代码吧 很清晰。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define N 500050
priority_queue <LL, vector <LL>, greater<LL> > Qa;
priority_queue <LL> Qb;
LL a[N], b[N];
int n, m;
const LL INF = 1e13;
int main() {
// freopen("in.txt", "r", stdin);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i ++) scanf("%I64d", a + i);
for (int i = 1; i <= n; i ++) scanf("%I64d", b + i);
LL st = 0, en = INF, ans = 0;
while (st <= en ) {
LL mid = (st + en) >> 1;
while ( !Qa.empty() ) Qa.pop();
while ( !Qb.empty() ) Qb.pop();
LL tmp = 0;
int sz = 0;
for (int i = 1; i <= n; i ++) {
Qa.push(a[i]);
LL tmp1 = Qa.top() + b[i] - mid;
LL tmp2 = Qb.empty() ? INF : b[i] - Qb.top();
if (tmp1 <= tmp2 && tmp1 <= 0) {
tmp += tmp1; sz ++;
Qb.push(b[i]);
Qa.pop();
}
else if (tmp2 < tmp1 && tmp2 < 0){
tmp += tmp2;
Qb.pop();
Qb.push(b[i]);
}
}
if (sz >= m) {
ans = tmp + m * mid;
en = mid - 1;
}
else {
st = mid + 1;
}
}
cout << ans << endl;
}