洛谷题目传送门c++
神仙思惟题仍是要写点东西才好。优化
这种很抽象的东西没有式子描述一下显然是下不了手的。
由于任何位置都以\(k\)为周期,因此咱们只用关心一个周期,也就是如下数都在膜\(k\)意义下。
设\(a_i\)表示\(i\)号区间长度;
对于上行列车(\(0\rightarrow n\))设\(p_0\)表示出发时刻,\(p_i(i\ge1)\)表示在\(i\)站停靠时间;
对于下行列车(\(0\leftarrow n\))设\(-q_0\)表示到站时刻,\(q_i(i\ge1)\)表示在\(i\)站停靠时间;
(转化成\(-q_0\)是为了后面表示方便)
用大写字母\(A,P,Q\)分别表示它们的前缀和。
若是某区间\(b_i=1\),则两列车的行驶时间区间不交,即
\[(P_{i-1}+A_{i-1},P_{i-1}+A_i)\cap(-Q_{i-1}-A_i,-Q_{i-1}-A_{i-1})=\emptyset\]
区间不交即端点不被包含,能够列出不等式(被取模了因此看着比较奇怪)
\[\begin{cases}P_{i-1}+A_i\le-Q_{i-1}-A_i\\P_{i-1}+A_{i-1}\ge-Q_{i-1}-A_{i-1}\end{cases}\]
记\(x=P_{i-1}+Q_{i-1}\),移项,解得\(x\in[-2A_{i-1},-2A_i]\)
由于\(P,Q\)是递增的,因此咱们的问题变成了:
有若干个限制区间\([l_i,r_i]\),你手头有一个数\(x\),初值任选,每次能够加一个非负整数使它落在区间内,求最少总共加多少能知足限制。spa
考虑这样一个贪心的决策过程:
假设咱们知道当前的\(x\),咱们须要统计它最少总共被加了多少。咱们看上一个限制区间。
若是\(x\)被上一个区间包含,那么咱们看上上个区间。
若是\(x\)没有被上一个区间包含,则\(x\)从区间的右端点加过来的代价最小,咱们继续对上上个区间的右端点进行决策。
发现咱们决策的中间状态只和区间右端点有关,因此咱们设\(f_i\)表示决策到第\(i\)个区间时,\(x=r_i\)的最小代价。
咱们对于每一个值维护最后一个没有包含这个值的区间编号,每次取出\(r_i\)对应的编号(记为\(j\)),用\(f_j+r_i-r_j\)更新\(f_i\),并将\([r_i+1,l_i-1]\)的编号所有设为\(i\)。
最后把全部的\(l_i\)丢进去查一下取个\(\min\)加上\(2A_n\)就是答案。
由于是区间设置因此能够珂朵莉树维护,不用离散化,由于随机数据的编号段数不会太大因此平均状况下跑得比线段树还快。
特判:若是\(2a_i>k\)那么puts("-1")
code
#include<bits/stdc++.h> #define LL long long #define R register int #define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin)) using namespace std; const int SZ=1<<19,N=1e5+9; char buf[SZ],*ie=buf+SZ,*ip=ie-1; inline int in(){ G;while(*ip<'-')G; R x=*ip&15;G; while(*ip>'-'){x*=10;x+=*ip&15;G;} return x; } int k,l[N],r[N];LL f[N];bool b[N]; struct Node{ int r;mutable int v; inline Node(R a,R b=0):r(a),v(b){} inline bool operator<(const Node&a)const{return r<a.r;} }; typedef set<Node>::iterator IT; set<Node>s; inline IT Split(R p){ IT i=s.lower_bound(Node(p)); return i->r!=p?s.insert(Node(p,i->v)).first:i; } inline void Set(R l,R r,R v){ if(l>r)return; IT il=Split(l-1),ir=Split(r); ir->v=v;s.erase(++il,ir); } inline LL Calc(R p){ R j=s.lower_bound(Node(p))->v; return j?f[j]+(p-r[j]+k)%k:0; } int main(){ R n=in();LL k=::k=in(),a=0,a1=0,ans=1e18; s.insert(Node(-1)); s.insert(Node(k-1)); for(R i=1;i<=n;++i){ a1=a;a+=in(); if(!(b[i]=in()&1))continue; if(2*(a-a1)>k)return puts("-1"),0; l[i]=(-2*a1%k+k)%k;r[i]=(-2*a%k+k)%k; f[i]=Calc(r[i]); if(l[i]>r[i])Set(r[i]+1,l[i]-1,i); else Set(0,l[i]-1,i),Set(r[i]+1,k-1,i); } for(R i=1;i<=n;++i) if(b[i])ans=min(ans,Calc(l[i])); cout<<ans+2*a<<endl; return 0; }