【带权二分】记录一些带权二分的一些题目
RT
带权二分主要是处理问题如在物品中选择k个,或者分k组,这些问题都有一个共性就是选得越多越好(满足单调性质)。对于平常的处理方法我们都是利用dp处理,将选择多少个作为一维度,但是在有些情况下,时空复杂度是容不下的。由于其具有单调性,那么我们可以利用决策单调性(或者斜率优化),或者用同样利用到单调性的二分来处理(有些时候要一起用到orz)。基本思想就是将原本越多越好的二分加上一些代价,使得并不能选得越多越好,而是在满足dp下优先选出最好的一些,当二分达到某个值的时候,也就满足了恰好k个或k组的限制条件。
显然满足考虑决策单调性来搞,决策单调性就是dp决策点单调不减。
搞一个二分单调队列,满足每个都管一段区间,且越队尾越管后面,也就是维护一个凸壳。感性理解就是再多加一个东西使得答案变得更优速度更慢,对于本题就是一个分组里的鱼变多使得答案更好的速度更慢,具体证明?orz orz orz ,遇到写个暴力dp看一下是不是决策单调就好,或者用用四边形不等式orz.
BZOJ2151种树
题意:有n 2e5颗树围成一圈,选每颗树有价值,不能选择相邻的两棵树,求恰好选m颗的价值 f[i][1/0][1/0]讨论到第i个,第i个选了没有,第一个选了没有。二分代价加到每颗树的价值上,记得记录下选了多少个。 !!!提示:在hdhd大神的hack之下,发现一个大漏洞,就是这里的二分范围。范围不能开太小了,开成[-1000,1000]是错误的。实际上大概应该开成绝对值之和?本题数据水,一组hdhd大神样例: 6 3 -1000 1000 -1000 -1000 1000 -1000#include<stdio.h> #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> typedef double db; const int maxn = 200005; struct oo{ int v; int x; }f[maxn][2][2];//前i个第i个选了没第1个选了没 bool operator<(const oo&x,const oo&y) { if(x.v!=y.v) return x.v < y.v; return x.x > y.x; } using namespace std; int n,m,a[maxn]; int as; bool dp(int ct) { memset(f,0,sizeof f); f[1][1][1] = {a[1]-ct,1}; f[1][0][1] = f[1][1][0] = {-0x3f3f3f3f,0}; for(int i=2;i<=n;i++) { f[i][0][0] = max(f[i-1][1][0],f[i-1][0][0]); f[i][0][1] = max(f[i-1][1][1],f[i-1][0][1]); f[i][1][0] = f[i-1][0][0]; f[i][1][0].v += a[i] - ct; f[i][1][0].x++; f[i][1][1] = f[i-1][0][1]; f[i][1][1].v += a[i] - ct; f[i][1][1].x++; } oo ans = max(f[n][1][0],max(f[n][0][1],f[n][0][0])); if(ans.x<=m) { as = ans.v+ct*m; return 1; } return 0; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } if(n/2<m) { puts("Error!"); return 0; } int L = -20005; int R = 20005,mid; while(L<=R) { mid = (L+R)>>1; if(dp(mid)) R = mid-1; else L = mid+1; } printf("%d",as); }
cf321E BZOJ5311贞鱼
题意:n个鱼选k个区间,每个区间有一个怨气值,求一种分区间方案使得怨气值总和最小。 带权二分消除掉选多少个区间的条件之后f[x] = min: f[y] + val(y+1,x),那么这个东西#include<stdio.h> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int maxn = 4e3 + 5; struct node{ int x,l,r; }qe[maxn]; int ql,qr; int n,k; int sm[maxn][maxn]; int g[maxn]; int val(int x,int y) { return ( sm[y][y] - sm[x-1][y] - sm[y][x-1] + sm[x-1][x-1] )/2; } int f[maxn]; int calc(int x,int y) { return f[x] + val(x+1,y); } int better(int x,int y,int k) { // x better than y j int fx = calc(x,k); int fy = calc(y,k); if(fx<fy||(fx==fy&&g[x]<g[y])) return 1; return 0; } int binary(int i,int j) { int l = qe[qr].l , r = n ,ans = 0; while(l<=r){ int mid = (l+r)>>1; if(better(i,j,mid)) ans = mid ,r = mid - 1; else l = mid + 1; } return ans; } void solve(int ct) { ql = qr = 1; qe[1] = (node){0,0,n}; for(int i=1;i<=n;i++) { ++qe[ql].l; if(qe[ql].l>qe[ql].r) ++ql; f[i] = calc(qe[ql].x,i)+ct; g[i] = g[qe[ql].x]+1; if(better(i,qe[qr].x,n)) { while(ql<=qr&&better(i,qe[qr].x,qe[qr].l)) --qr; if(ql>qr) qe[++qr] = (node){i,i,n}; else { int x = binary(i,qe[qr].x); qe[qr].r = x-1; qe[++qr] = (node){i,x,n}; } } } } char buf[1<<20],*p1,*p2; #define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++) inline int R() { char t=GC; int x=0; while(!isdigit(t)) t=GC; while(isdigit(t)) x=x*10+t-48,t=GC; return x; } int main() { n = R(); k=R(); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { sm[i][j] = R(); sm[i][j] += sm[i-1][j] +sm[i][j-1] -sm[i-1][j-1]; } } int L = 0; int R = 5000 , mid,ans = 0; while(L<=R) { int mid = (L+R)>>1; solve(mid); if(g[n]<=k) ans = mid , R = mid - 1; else L = mid + 1; } solve(ans); printf("%d",f[n]-k*ans); }