这个东西好神仙啊.jpgnode
$Orz laofu$ios
给你$n$个老鼠,$m$个洞,求一个知足要求的匹配的代价。优化
拥有一个限制:只能向左走。spa
直接排序便可。code
无限制。排序
显然能够给出DP方程:$f[i][j]$表示,前$i$个位置,有 $ j $ 个洞须要匹配,其中$j$能够为负,表示的意义为有$-j$个老鼠须要匹配。get
显然,这个一种常见的思路是将每个距离拆开看,为$|x_i-y_j|$。string
因为在匹配过程当中,交叉匹配必定会有不比它差的非交叉匹配方案,因此咱们将全部的交叉匹配方式去掉。it
可是,显然这样转移是没有办法优化掉的,是满的$n^2$,因此咱们考虑如何经过分析性质将其优化掉。io
如今咱们发现,对于上述DP方程,仅有直接覆盖决策和区间抬升。
简易代码以下:
stack sz,sf; int tag1=0,tag2=0,f0=0; for(int i=1;i<=n;i++) { if(op[i]==1)//洞 { tag1-=a[i],tag2+=a[i]; sf.push(f0+a[i]-tag2); f0=min(sz.top()+tag1-a[i],f0);sz.pop(); }else // 鼠 { tag1+=a[i],tag2-=a[i]; sz.push(f0+a[i]-tag1); f0=sf.top()+tag2-a[i];sf.pop(); } }
显然,对于这种匹配问题,必定不会存在匹配交叉的状况,因此咱们考虑在此的基础上进行贪心。
对于上述DP方程,咱们给出以下差分结果:
$\begin{cases} d[i][j]=f[i][j]-f[i][j-1] , j>0 \ d[i][j]=f[i][j]-f[i][j+1],j<0\end{cases}$
那么给出以下转移,因为咱们发现,经过上述表达,只能表达出$f[i][j]$之间的关系,没法准确的表达出$f[i][j]$,因此咱们须要维护一个$f[i][0]$来获得因此的关系。
那么给出转移:
对于老鼠:$f[i][0]=f[i-1][0]+d[i-1][1]+a[i],d[i][j]=\begin{cases}d[i-1][j+1] , j>0||j<-1\-d[i-1][1]-x[i]\times 2\end{cases}$
对于洞:$f[i][0]=\min(f[i-1][0],f[i-1][0]+d[i-1][-1]+a[i])$
拆开看:
这个东西显然就能够用上面的那个栈的作法用差分意义理解了...
有这个东西,能够发现这个DP是具备凸性的,也就是说,对于这个DP来讲,$d[i][j]$在$j>0$时单调递增,在$j<0$时单调递减。
因此直接用堆来维护一下最小的$d[i][j],j>0$和最小的$d[i][j],j<0$便可。
实现代码以下:
priority_queue<int>q0,q1; for(int i=1;i<=n;i++) if(op[i]==1)//洞 { if(q0.top()+a[i]<0) { f0=f0+q0.top()+a[i]; q1.push(-q0.top()-a[i]*2); q0.pop(); }else q1.push(-a[i]); }else // 鼠 { f0=f0+q1.top()+a[i]; q0.push(-q1.top()-a[i]*2); q1.pop(); }
另外的一种分析方式:
对于当前的两个堆,分别至关因而对于鼠和对于洞的匹配集合,每次强制老鼠匹配一个还没有匹配的,在他左边的洞,而后能够在以后的操做中将此次匹配反悔,变成匹配以后的某一个洞。
如今,对于给定问题,老鼠只能向左走,而且代价为$ x_i-y_j+w_j $,不必定每一个老鼠都进入洞中,求最大代价。
好像这个题能够随便作的样子...
直接按照$a_i$从左到右的顺序,维护一个对于洞的堆,其中的比较关键字是按照$w_j-y_j$从大到小排序。
而后每次取出堆顶进行匹配便可...
对于每一个洞,有一个容量限制,也就是每一个洞能够容纳$b_i$个老鼠。
好的,咱们接下来考虑这个题如何处理...
只须要强制往左跑,和强制往右跑的构成的堆的交集作一发便可。
这个东西的时间复杂度显然是$O(n\log n)$的。
对此的证实:显然我不会。
显然,只须要在维护堆的时候,传进两个参数,分别表示剩余的容量,和相应的代价,而后按照第二维维护最小值便可。
每次把对应容量减去,若是为$0$就再也不压入...
大体代码:
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <cstdlib> #include <queue> #include <iostream> #include <bitset> using namespace std; #define N 200005 #define ll long long const long long inf = 1ll<<40; struct node{int op,lim;ll x,w;}a[N]; bool cmp(const node &a,const node &b){return a.x<b.x;} int tot,n,m;ll f0,sum; priority_queue<pair<ll ,int > ,vector<pair<ll ,int > > ,greater<pair<ll ,int > > >q1; priority_queue<ll ,vector<ll > ,greater<ll > >q0; int main() { scanf("%d%d",&n,&m); for(int i=1,x;i<=n;i++)scanf("%d",&x),a[i]=(node){0,0,x,0};tot=n; for(int i=1;i<=m;i++)tot++,scanf("%lld%lld%d",&a[tot].x,&a[tot].w,&a[tot].lim),a[tot].op=1,sum+=a[tot].lim; a[++tot]=(node){1,n,-inf,0};a[++tot]=(node){1,n,inf,0}; if(sum<n)return puts("-1"),0;sort(a+1,a+tot+1,cmp); for(int i=1;i<=tot;i++) if(a[i].op==1)//洞 { int t=a[i].lim; while(!q0.empty()&&t&&q0.top()+a[i].x<0) { f0=f0+q0.top()+a[i].x; q1.push(make_pair(-q0.top()-a[i].x*2,1)); q0.pop();t--; } if(t)q1.push(make_pair(-a[i].x,t)); }else // 鼠 { pair<ll ,int > tmp=q1.top();q1.pop(); f0=f0+tmp.first+a[i].x; q0.push(-tmp.first-a[i].x*2);tmp.second--; if(tmp.second)q1.push(tmp); } printf("%lld\n",f0); }
每一个老鼠能够无限分身,可是分出来的身必定要进入洞中,每一个洞有一个容量无限,可是至少有一只老鼠。
而后彷佛正解也不是很难,咱们只须要这样考虑,对于每一个老鼠的权值,分红两类,一部分,容量为$1$,费用为$-\infty+d[i][1]+x_i\times 2$,一部分容量为$\infty$,费用为$d[i][1]+x[i]\times 2$
而后对于每一个洞,也就一样,当作Pro4中容量上限为$\infty$来作。
而后咱们考虑到,两个容量$\infty$之间匹配,必定不优,因此不存在这种匹配状况。
大体代码:
priority_queue<pair<int ,int > >q0,q1; for(int i=1;i<=n;i++) if(op[i]==1)//洞 { pair<int ,int >tmp;tmp=q0.top();q0.pop(); if(tmp.first+a[i]-inf<0) { f0=f0+tmp.first+a[i]; q1.push(make_pair(tmp.first,1));tmp.second--; if(!tmp.second)tmp=q0.top(); }else q1.push(make_pair(a[i]-inf,1)); while(tmp.first+a[i]<0) { f0=f0+tmp.first+a[i];q0.pop(); q1.push(make_pair(-tmp.first-a[i]*2,1));tmp.second--; if(tmp.second)q0.push(tmp); } q1.push(a[i],inf); }else // 鼠 { pair<int ,int >tmp;tmp=q1.top();q1.pop(); if(tmp.first+a[i]-inf<0) { f0=f0+tmp.first+a[i]; q0.push(make_pair(tmp.first,1));tmp.second--; if(!tmp.second)tmp=q1.top(); }else q0.push(make_pair(a[i]-inf,1)); while(tmp.first+a[i]<0) { f0=f0+tmp.first+a[i];q1.pop(); q0.push(make_pair(-tmp.first-a[i]*2,1));tmp.second--; if(tmp.second)q1.push(tmp); } q0.push(a[i],inf); }
在最基础的模型上增长,每一个洞有一个权值$w_i$,老鼠进洞的代价变为:$|x_i-y_i|+w_i$
想明白了,也不是很难。
只须要考虑到上述式子在不一样状况下,代价不一样,分别是$w_i-y_i$和$w_i+y_i$,而后分别做为洞和老鼠的反悔状况。
这样的意义表明着,老鼠即便匹配了右侧的一个洞,也可能会反悔去匹配更右侧的一个洞,由于这样可能代价更小。
其余的东西却是没啥区别。
简易代码以下:
priority_queue<int>q0,q1; for(int i=1;i<=n;i++) if(op[i]==1)//洞 { if(q0.top()+a[i]+w[i]<0) { f0=f0+q0.top()+a[i]+w[i]; q1.push(-q0.top()-a[i]*2); q0.pop(); q0.push(-a[i]-w[i]); }else q1.push(-a[i]+w[i]); }else // 鼠 { f0=f0+q1.top()+a[i]; q0.push(-q1.top()-a[i]*2); q1.pop(); }
咱们能够考虑,若是模型转化到树上了,如何解决。
(可能大概这个题能够被一眼秒掉了。
直接把堆换成可并堆,一样,它也不能交叉匹配,这里的交叉匹配是指在同一条链上时,二者交叉匹配。
没有简易代码
其实仍是最基础的模型,上面加上了容量和额外代价。
直接把上面Pro 6和Pro 4的作法结合起来便可。
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <cstdlib> #include <queue> #include <iostream> #include <bitset> using namespace std; #define N 200005 #define ll long long const long long inf = 1ll<<40; struct node{int op,lim;ll x,w;}a[N]; bool cmp(const node &a,const node &b){return a.x<b.x;} int tot,n,m;ll f0,sum; priority_queue<pair<ll ,int > ,vector<pair<ll ,int > > ,greater<pair<ll ,int > > >q1,q0; int main() { scanf("%d%d",&n,&m); for(int i=1,x;i<=n;i++)scanf("%d",&x),a[i]=(node){0,0,x,0};tot=n; for(int i=1;i<=m;i++)tot++,scanf("%lld%lld%d",&a[tot].x,&a[tot].w,&a[tot].lim),a[tot].op=1,sum+=a[tot].lim; a[++tot]=(node){1,n,-inf,0};a[++tot]=(node){1,n,inf,0}; if(sum<n)return puts("-1"),0;sort(a+1,a+tot+1,cmp); pair<ll ,int > tmp; for(int i=1;i<=tot;i++) if(a[i].op==1)//洞 { int t=a[i].lim; while(!q0.empty()&&t&&q0.top().first+a[i].w+a[i].x<0) { tmp=q0.top();q0.pop(); int now=min(t,tmp.second); f0=f0+(ll)now*(tmp.first+a[i].x+a[i].w); q1.push(make_pair(-tmp.first-a[i].x*2,now)); t-=now;tmp.second-=now;if(tmp.second)q0.push(tmp); } if(a[i].lim!=t)q0.push(make_pair(-a[i].x-a[i].w,a[i].lim-t)); if(t)q1.push(make_pair(-a[i].x+a[i].w,t)); }else // 鼠 { tmp=q1.top();q1.pop(); f0=f0+tmp.first+a[i].x; q0.push(make_pair(-tmp.first-a[i].x*2,1));tmp.second--; if(tmp.second)q1.push(tmp); } printf("%lld\n",f0); }
没看懂这题在说些啥
给一个序列,要求选出$K$个区间,每一个区间不相交,求最大和。
直接线段树维护区间连续最大值,每次区间取相反数便可。
看样子像是Pro 9 + Pro 10的合体,反正不太可写...
预估代码会和蜀道难有一拼了.jpg
暴力的话,费用流直接建图应该有$40\sim60$分不等,我不知道我本身哪里写挂掉了,wa了好几个点...
因为咱们发现,随着天数的增长,选择的蔬菜的集合只会变成原先的父集,也就是不会退流,因此直接使用线段树维护增广的合法性便可,剩下的就是堆+贪心了。
有一棵$n $个点的树,$m$我的站在树根。每条边有一个边权。如今每一个人均可以任意行走,通过一条边就要付出对应边权的代价。问最小代价使得每条边至少被一我的通过。$n \le 10^5,m \le 50$。
直接DP不解释。