Pluto_Rabbit's Blog

蒟蒻妹纸一枚,轻拍

【学习】CDQ分治

Jacinth posted @ 2017年1月08日 10:32 in Lerning with tags 学习 bzoj CDQ分治 Lerning , 1036 阅读

蒟蒻真是什么都不会!

久仰CDQ分治大名QAQ

感觉后面的小哥cool是个CDQ分治大师

结果拖到现在才好好地学习一番TAT

蒟蒻太差只写了三道不要喷啊啊啊啊

感觉理解得并不好TAT

BZOJ:

No. 状态
1492 Accepted
2726 Accepted
3461 Accepted

BZOJ-1492: [NOI2007]货币兑换Cash

应该是最经典的CDQ分治了吧

从《Cash》谈一类分治算法的应用

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-9;
const int NN = 100005;
int n,top,stk[NN];
double f[NN];
struct P
{
	double x,y,a,b,k,rate;
	int num;
}p[NN],t[NN];
double Get_k(int xx,int yy)
{
	if (!yy) return -1e20;
	if (fabs(p[xx].x-p[yy].x) < eps) return 1e20;
	return (p[yy].y-p[xx].y) / (p[yy].x-p[xx].x);
}
bool cmp(P xx,P yy) { return xx.k > yy.k; }

void Solve(int l,int r)
{
	if (l == r)
	{
		f[l] = max(f[l],f[l-1]);
		p[l].y = f[l]/(p[l].a*p[l].rate + p[l].b);
		p[l].x = p[l].rate * p[l].y;
		return;
	}
	int mid = (l+r) >> 1, l1 = l, l2 = mid+1, j = 1;
	for (int i=l;i<=r;++i)
		if (p[i].num <= mid) t[l1++] = p[i];
		else t[l2++] = p[i];
	for (int i=l;i<=r;++i) p[i] = t[i];
	Solve(l,mid);
	top = 0;
	for (int i=l;i<=mid;++i)
	{
		while (top > 1 && Get_k(stk[top-1],stk[top]) < Get_k(stk[top-1],i)+eps) --top;
		stk[++top] = i;
	}
	stk[++top] = 0;
	for (int i=mid+1;i<=r;++i)
	{
		while (j < top && Get_k(stk[j],stk[j+1]) + eps > p[i].k) ++j;
		f[p[i].num] = max(f[p[i].num],p[stk[j]].x*p[i].a+p[stk[j]].y*p[i].b);
	}
	Solve(mid+1,r);
	l1 = l, l2 = mid+1;
	for (int i=l;i<=r;++i)
		if (((p[l1].x < p[l2].x || (fabs(p[l1].x-p[l2].x) < eps && p[l1].y < p[l2].y)) || l2 > r) && l1 <= mid)
			t[i] = p[l1++];
		else t[i] = p[l2++];
	for (int i=l;i<=r;++i) p[i] = t[i];
}


int main()
{
	scanf("%d%lf",&n,&f[0]);
	for (int i=1;i<=n;++i)
	{
		scanf("%lf%lf%lf",&p[i].a,&p[i].b,&p[i].rate);
		p[i].k = -p[i].a / p[i].b;
		p[i].num = i;
	}
	sort(p+1,p+1+n,cmp);
	Solve(1,n);
	printf("%.3lf\n",f[n]);
	return 0;
} 

不要问我为什么后面两道的代码也是一个格式的

BZOJ-2726: [SDOI2012]任务安排

设$f[i]$为将前$i$个任务划分完成的最小费用,$T[i],F[i]$分别表示$t$和$f$的前缀和,则不难写出转移方程式:

$$f[i]=min\{ f[j]+(F[n]-F[j])\times(T[i]-T[j]+S) \},(1<=j<=i-1)$$

令$p[i].x=F[i],p[i].y=f[j]-F[n]\times T[j]+F[j]\times T[j]-S\times F[j],p[i].k=T[i]$则有:

$$f[i]=min\{ -p[i].k \times p[j].x + p[j].y\}$$

出题人太强了打得蒟蒻只能用CDQ惹

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 1e18;
const int NN = 500005;
int F[NN],T[NN],st[NN],n,S,top;
LL f[NN];
struct P
{
	LL x,y,k;
	int num;
}q[NN],t[NN];
bool cmp(P xx,P yy)
{
	return xx.k < yy.k;
}
inline LL U(int j,int k) { return (LL)q[k].y-q[j].y;}
inline LL D(int j,int k) { return (LL)q[k].x-q[j].x;}
inline bool Check(const P&a,const P& b)
{
	return a.x<b.x || a.x==b.x&&a.y<b.y;
}
void Solve(int l,int r)
{
	if (l == r)
	{
		q[l].x = (LL) F[l];
		q[l].y = (LL) f[l] - (LL)F[n]*T[l] + (LL)F[l]*T[l] - (LL)S*F[l];
		return;
	}
	int mid = (l+r) >> 1, l1 = l, l2 = mid+1,j = 1;
	for (int i=l;i<=r;++i)
		if (q[i].num > mid) t[l2++] = q[i]; else t[l1++] = q[i];
	for (int i=l;i<=r;++i) q[i] = t[i];
	Solve(l,mid);
	top = 0;
	for (int i=l;i<=mid;++i)
	{
		while (top>1 && (LL)U(st[top-1],st[top])*D(st[top-1],i)>(LL)U(st[top-1],i)*D(st[top-1],st[top]))
			top--;
		st[++top] = i;
	}
	for (int i=mid+1;i<=r;++i)
	{
		while (j<top && U(st[j],st[j+1]) < (LL)q[i].k*D(st[j],st[j+1])) ++j;
		f[q[i].num] = min(f[q[i].num],-(LL)q[i].k*q[st[j]].x+q[st[j]].y+(LL)F[n]*(T[q[i].num]+S));
	}
	Solve(mid+1,r);
	l1 = l, l2 = mid+1;
	for (int i=l;i<=r;++i)
	{
		if ((l2 > r || Check(q[l1],q[l2])) && l1<=mid) t[i] = q[l1++]; else t[i] = q[l2++];
	}
	for (int i=l;i<=r;++i) q[i] = t[i];
}

int main()
{
	scanf("%d%d",&n,&S);
	for (int i=1;i<=n;++i) scanf("%d%d",&T[i],&F[i]), T[i] += T[i-1], F[i] += F[i-1];
	for (int i=1;i<=n;++i) q[i].num = i, q[i].k = T[i], f[i] = INF;
	sort(q+1,q+1+n,cmp);
	Solve(0,n);
	printf("%lld\n",f[n]);
	return 0;
}

BZOJ-3461: Jry的时间表

$f[i]$表示$[1,i]$操作一次,且在$[j+1,i]$处操作

分把$[j+1,i]$改为$b[i]$或改为$b[j]$

其实把数列倒一下就可以了

$$f[i] = max\{ sum[j] + b[i] \times (i-j) \} = b[i] \times i + max\{ -j \times b[i] + sum[j]\} (0<= j < i)$$

上CDQ分治优化即可,最后:

$$ans = max\{ fl[i]-sum[i]+fr[j]+sum[j-1] \}(0 <= i < j <= n)$$

拆开来然后维护前缀最大的$fl[i]-sum[i]$就可以辣

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int NN = 500010;
const double eps = 1e-9;
int n,a[NN],b[NN],stk[NN],top;
LL sum[NN],f[NN],fl[NN],fr[NN],pre,ans;
struct P
{
	int x,num; LL y;
}p[NN],t[NN];
inline double Get_k(int x,int y)
{
	return (double)(p[x].y-p[y].y)/(double)(p[x].x-p[y].x);
}
inline bool cmp(P xx,P yy)
{
	if (xx.x == yy.x) return xx.y > yy.y; 
	else return xx.x < yy.x;
}
inline double Pos(int x,int y) { return (double)(sum[x]-sum[y])/(double)(x-y); }
inline LL ASK(int x)
{
	int l=1,r=top-1,tmp=top,mid;
	while(l<=r)
	{
		mid=(l+r)>>1;
 	   if ((double)x>Pos(stk[mid],stk[mid+1])) r=(tmp=mid)-1; else l=mid+1;
	}
	return sum[stk[tmp]]-(LL)stk[tmp]*x;
}
void Solve(int l,int r)
{
	if (l == r)
	{
		p[l].x = b[l+1];
		p[l].y = sum[l]-(LL)b[l+1]*l;
		return;
	}
	int mid = (l+r) >> 1, l1 = l, l2 = mid+1, j = 1;
	for (int i=l;i<=r;++i)
		if (p[i].num <= mid) t[l1++] = p[i];
		else t[l2++] = p[i];
	for (int i=l;i<=r;++i) p[i] = t[i];
	Solve(l,mid);
	top = 0;
	for (int i=l;i<=mid;++i)
	{
		while (top > 1 && Get_k(stk[top-1],stk[top]) <= Get_k(stk[top-1],i)+eps) --top;
		stk[++top] = i;
	}
	for (int i=mid+1;i<=r;++i)
	{
		while (j<top && (p[stk[j]].y-p[stk[j+1]].y) <= ((LL)p[i].num*(p[stk[j+1]].x-p[stk[j]].x))) ++ j;
		f[p[i].num] = max(f[p[i].num],(LL)p[stk[j]].x*p[i].num+p[stk[j]].y);
	}
	Solve(mid+1,r);
	l1 = l, l2 = mid+1;
	for (int i=l;i<=r;++i)
		if (((p[l1].x < p[l2].x || (fabs(p[l1].x-p[l2].x) < eps && p[l1].y < p[l2].y)) || l2 > r) && l1 <= mid)
			t[i] = p[l1++];
		else t[i] = p[l2++];
	for (int i=l;i<=r;++i) p[i] = t[i];
}
void Work()
{
	for (int i=1;i<=n;++i) sum[i] = sum[i-1] + a[i], p[i].num = i;
	top = 0;
	for (int i=0;i<=n;stk[++top] = i++)
	{
		if (i)
			f[i] = (LL)b[i]*i+ASK(b[i]);
		while (top > 1 && Pos(i,stk[top]) > Pos(stk[top],stk[top-1])) --top;
	}
	sort(p+1,p+1+n,cmp);
	Solve(0,n);	
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%d",&a[i]);
	for (int i=1;i<=n;++i) scanf("%d",&b[i]);
	Work();
	for (int i=1;i<=n;++i) fl[i] = f[i];
	for (int i=1,j=n;i<j;++i,--j) swap(a[i],a[j]), swap(b[i],b[j]);
	Work();
	for (int i=1;i<=n;++i) fr[n-i+1] = f[i];
	for (int i=1;i<=n;++i) sum[i] = sum[i-1] + a[n-i+1];
	ans = sum[n];
	for (int i=1;i<=n;++i) ans = max(fr[i]+sum[i-1]+pre,ans), pre = max(pre,fl[i]-sum[i]);
	printf("%lld\n",ans);
	return 0;
}

不知道为什么A掉这道题成就感这么强WAW

Avatar_small
how to delete an ins 说:
2022年8月08日 19:19

Completely deleting your Instagram Account will get all your activity connection and activities through this account to be removed permanently, and also Comments, Likes, photos, Videos and your follower will be completely removed from the Instagram Account. how to delete an instagram account This process of deleting an Instagram Account is nowhere a recoverable process, thus if you want to get an account removed you may need to think twice and confirm, and make sure you have got the password for the Instagram Account that you want to delete from your MAC or Android, else you won’t be able to proceed further.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter