BZOJ1568: [JSOI2008]Blue Mary开公司

可以平衡树或线段树维护斜率来做。还有一种线段树直接打标记的做法(李超线段树):线段树每个节点存一条线段作为标记,打标记时如果已有标记,则把占优区间小的那个线段下放。

#include<cstdio>
#include<algorithm>
#define N 50000
#define M (l+r>>1)
#define P (k<<1)
#define S (k<<1|1)
#define L l,M,P
#define R M+1,r,S
#define Z \
int l=0,int r=N-1,int k=1
using namespace std;
struct node{
	double s,t;
	double val(int x){
		return s*x+t;
	}
}e[N*2],*u=e,*a[N*4];
void apply(node*s,Z){
	if(!a[k])a[k]=s;
	else{
		if(a[k]->val(M)<s->val(M))
			swap(a[k],s);
		if(l!=r)
			a[k]->s<s->s?apply(s,R):apply(s,L);
	}
}
double query(int s,Z){
	double v=a[k]?a[k]->val(s):0;
	if(l!=r)
		v=max(v,s<=M?query(s,L):query(s,R));
	return v;
}
int i,m;
char k[8];
double s,t;
int main(){
	for(scanf("%d",&m);m;--m){
		scanf("%s",k);
		if(*k==80){
			scanf("%lf%lf",&t,&s);
			apply(&(*u++=(node){s,t}));
		}
		else{
			scanf("%d",&i);
			printf("%d\n",(int)query(i-1)/100);
		}
	}
}

之前写的是一个奇怪的做法……

答案序列一定是个下凸壳,因此添加的等差数列与其之差是个单峰函数,可以先三分求出最值,再二分求出零点,然后用线段树,将得到的区间修改为一个等差数列。

要降低复杂度的话可以把三分和二分写到线段树里面……

#include<cstdio>
#define Z \
int l=1,int r=N,int k=1
#define N 50000
#define M (l+r>>1)
#define P (k<<1)
#define S (k<<1|1)
#define K l,r,k
#define L l,M,P
#define R M+1,r,S
double a[N*4],b[N*4];
void devolve(Z){
	if(b[k]){
		a[S]=a[k]+(M-l+1)*(b[P]=b[S]=b[k]);
		a[P]=a[k],b[k]=0;
	}
}
double query(int s,Z){
	if(l!=r){
		devolve(K);
		return s<=M?query(s,L):query(s,R);
	}
	return a[k];
}
void amend(double u,double v,int s,int t,Z){
	if(s==l&&t==r)
		a[k]=u,b[k]=v;
	else{
		devolve(K);
		if(t<=M)
			amend(u,v,s,t,L);
		else if(s>M)
			amend(u,v,s,t,R);
		else{
			amend(u,v,s,M,L);
			amend(u+(M-s+1)*v,v,M+1,t,R);
		}
	}
}
double s,t;
int i,j,m,r,l;
char k[8];
void solve(){
	scanf("%lf%lf",&s,&t);
	l=1,r=N;
	while(l!=r){
		i=l+(r-l)/3;
		j=r-(r-l)/3;
		if(t*(i-j)<query(i)-query(j))
			l=i+1;
		else r=j-1;
	}
	if(s+t*l-t>query(l)){
		r=l,l=1;
		while(l!=r){
			i=l+r>>1;
			if(s+t*i-t>query(i))
				r=i;
			else l=i+1;
		}
		j=l,r=N;
		while(l!=r){
			i=l+r+1>>1;
			if(s+t*i-t>query(i))
				l=i;
			else r=i-1;
		}
		amend(s+t*j-t,t,j,l);
	}
}
int main(){
	for(scanf("%d",&m);m;--m){
		scanf("%s",k);
		if(*k==80)
			solve();
		else{
			scanf("%d",&i);
			printf("%d\n",(int)query(i)/100);
		}
	}
}

最后吐槽一句这题啥破样例啊。

转载于:https://www.cnblogs.com/f321dd/p/5745212.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
VMProtect使用说明 一. 接口说明 //始保护处标记(对应于功能设置:反调试、内存保护等等) VMProtectBegin(const char *); //始虚拟化代码处标记(包括保护设置) VMProtectBeginVirtualization(const char *); //始变异代码处标记(包括保护设置) VMProtectBeginMutation(const char *); //始虚拟+代码变异标记处 VMProtectBeginUltra(const char *); VMProtectBeginVirtualizationLockByKey(const char *); VMProtectBeginUltraLockByKey(const char *); //保护结束处标记 VMProtectEnd(void); //检测调试 BOOL VMProtectIsDebuggerPresent(BOOL); //检测虚拟机 BOOL VMProtectIsVirtualMachinePresent(void); //映像文件CRC校验 BOOL VMProtectIsValidImageCRC(void); //解密被保护的名为字符串A char * VMProtectDecryptStringA(const char *value); //解密被保护的名为字符串W wchar_t * VMProtectDecryptStringW(const wchar_t *value); 二. 使用方法 1. 保护函数必须有始有终出现 VMProtectBegin、 VMProtectBeginVirtualization、 VMProtectBeginMutation、 VMProtectBeginUltra 必须有相对应的VMProtectEnd结束。 比如: void FunName() { VMProtectBegin(“FunName”); //最好用函数名,否则会出现重名冲突 ..... VMProtectEnd(); } 2. 保护的单元是函数,而不是整个EXE代码 比如: //不保护 void fun1(char* msg) { char* szmsg = "fun1 none vm"; OutputDebugString( szmsg ); } //虚拟化保护 int fun2( int x, int y ) { int n = x + y; VMProtectBeginVirtualization("fun2"); OutputDebugString( "x+y= %d" ); VMProtectEnd(); return n; } //虚拟化和变异保护 void fun3() { VMProtectBeginUltra("fun3"); fun1("fun3 call fun1"); VMProtectEnd(); fun2( 2, 4 ); } 3. 保护嵌套情况 void main() { char* szMsg = "proxxb vm sapmle!"; //不被保护 VMProtectBegin("main"); //被保护 OutputDebugString( "vm protect test." ); //被保护 fun1(szMsg); //函数内部自己去保护 fun2( 2, 4 ); //函数内部自己去保护 fun1(szMsg); //函数内部自己去保护 fun3(); //不被保护 VMProtectEnd(); getchar(); //不被保护 } 4. 字符串保护 应使用VMProtectDecryptStringA或VMProtectDecryptStringW函数保护名为字符串,被保护后,明晚字符串不再出现在内存,除非被解密的那一刻! char* Decrypt( char* key, char* buffer, long length ) { VMProtectBeginUltra("Decrypt"); .... VMProtectEnd(); } 调用方法: Decryp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值