19-11-07-~

怕被喷,于是写个反思。

ZJ:

马上就结束了,不管是如何……如何……

T1看看,写一个暴力,然后开始寻求规律,然后也没找出来。

T2先码了一个大暴力,然后发现可以$\Theta(N^2)$dp,然后有qj了一点分。

T3先想的是把每一个MagicStone的贡献单独考虑,后来过不了大样例,发现错了,要容斥。

于是去码暴力,最后没弄完。如果写完了就有$10$分

没办法,就再补补吧。

26
Miemeng 30
00:59:58
50
00:59:59
0
00:59:59
80
00:59:59

终于有题解la:

好!

T1:

大神秒切蒟蒻不会的题。

$20\%$算法:

直接归并即可。

复杂度很高:$O(NM)$

$100\%$算法:

有两个做法法法法法法法法法法法法。

法1:

我们可以粘题解通过当前数的位置得到
这个数在另一个序列的期望位置。假设当前的数为 $x$ ,期望位置的数
为 $y$ ,下一个数为 $z$ ,那么 $z \leq x \leq y$ 时 $x$ 就是答案,否则比较一下大小,往两边跳。

法2:

首先我们考虑如何减小问题规模。

我们想想暴力可以怎么搞。

往一个数据结构里扔数,并维护$size$,如果正好为$k$,我们就把$k$输出来。

那前面的呢?弃掉了。

于是我们是不是可以通过弃掉前面的数来使后面的数正确呢?

显然可以。

于是我们考虑加速这个过程,一个一个弃不慢么

那么分治的思想告诉我们要用中点以平衡复杂度。

考虑两个区间$[l_a,r_a],[l_b,r_b]$的第$\frac{k}{2}$个数

如果$A_{k/2}<B_{k/2}$,那么$A_{k/2}$及之前的可以全部弃掉了。

问题转换为在$[l_a+k/2+1,r_a],[l_b,r_b]$中找第$\frac{k}{2}$个数,问题规模就缩小了一半。

重复上述操作直到你去世可以一步出答案。

$A_{k/2} \geq B_{k/2}$同理。

#include <iostream>
#include <cstring>
#include <cstdio>
#define N 555555

using namespace std;

int pn,arr[N],brr[N],qn;

int read() {
	char c=getchar();
	int x = 0;
	for(;c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar())x=x*10-'0'+c;
	return x;
}
int solve(int kth,int l1,int r1,int l2,int r2){
//	cout<<kth<<" "<<l1<<" "<<r1<<" "<<l2<<" "<<r2<<endl;
	int hlf=kth/2;
	if(l1>r1){
		return brr[l2+kth-1];
	}
	else if(l2>r2){
		return arr[l1+kth-1];
	}
	else if(kth == 1){
		int ans=0x7fffffff;
		if(l1<=r1) ans=min(ans,arr[l1]);
		if(l2<=r2) ans=min(ans,brr[l2]);
		return ans;
	}
	else if(l1+hlf-1<=r1 && l2+hlf-1<=r2) {
		if(arr[l1+hlf-1] < brr[l2+hlf-1])
			 return solve(kth-hlf,l1+hlf,r1,l2    ,r2);
		else return solve(kth-hlf,l1    ,r1,l2+hlf,r2);
	}
	else if(l1+hlf-1<=r1){
		return solve(kth-hlf,l1+hlf,r1,l2,r2);
	}
	else if(l2+hlf-1<=r2){
		return solve(kth-hlf,l1,r1,l2+hlf,r2);
	}
	else {
		puts("µ");
		return 0;
	}
}

int main(){
#ifndef LOCAL
	freopen("median.in" ,"r",stdin);
	freopen("median.out","w",stdout);
#endif
	int opt,a,b,c,d;
	pn=read();qn=read();
	for(int i=1;i<=pn;i++)
		arr[i]=read();
	for(int i=1;i<=pn;i++)
		brr[i]=read();
	for(int i=1;i<=qn;i++){
		opt=read();
		if(opt==1){
			a=read(),b=read(),c=read();
			if(a==0) arr[b]=c;
			else     brr[b]=c;
		}
		else{
			a=read(),b=read(),c=read(),d=read();
			int k=(b-a+d-c+2+1)/2;
			printf("%d\n",solve(k,a,b,c,d));
		}
	}
}

T2:

%%%toot

上面的大聚聚暴锤正解。

正解:线段树维护单调栈。复杂度:$\Theta(N \log N)$

巨解:单调栈自己维护自己。复杂度:$\Theta(N)$

于是……

首先我们知道如何写暴力$dp$。

//min

#include <iostream>
#include <cstring>
#include <cstdio>
#define N 222222
#define LL long long

using namespace std;

int pn;
int arr[N];
int a,b,c,d;
LL dp[N];

LL f(LL x){
	return x*x*x*a+x*x*b+x*c+d;
}
LL ans=0;
namespace qj{
	void work(){
		bool is_upd=0;
		LL minn=0x7fffffff;
		for(int i=1;i<=pn;i++){
			minn=min(1ll*arr[i],minn);
			if(f(arr[i])>0){
				is_upd=1;
				ans+=f(arr[i]);
			}
		}
		if(!is_upd){
			ans=f(minn);
		}
		cout<<ans<<endl;
	}
}
int main(){
#ifndef LOCAL
	freopen("min.in" ,"r",stdin);
	freopen("min.out","w",stdout);
#endif
	ios_base::sync_with_stdio(false);
	cin>>pn>>a>>b>>c>>d;
	for(int i=1;i<=pn;i++)
		cin>>arr[i];
	if(a==0 && b==0 && c<=0 ){
		qj::work();
		return 0;
	}
	LL minn=0x7fffffff;
	for(int i=1;i<=pn;i++){
		minn=min(minn,1ll*arr[i]);
		dp[i]=f(minn);
	}
	for(int i=1;i<=pn;i++){
		minn=0x7fffffff;
		for(int j=i+1;j<=pn;j++){
			minn=min(minn,1ll*arr[j]);
			dp[j]=max(dp[j],dp[i]+f(minn));
		}
	}
	cout<<dp[pn]<<endl;
}

于是我们考虑如何优化$dp$过程

方法1:

$k$常优化。

一般遇到这种题出题人都$kuku$,

我们把第二层循环搞一搞,让它只跑$k$次

复杂度$\Theta(kN)$。

你可以试试$300 \sim 500$

方法2:

单调栈,因为我们要维护区间最小值于是考虑这个数据结构。

我们发现单调栈一段区间内的最小值是一定的。

于是先维护最小值,

然后$dp$一定从这段区间内的最大dp值转移过来。

于是维护每个区间的最大$dp+f(\min\{l,r\})$

那么我们就会被题解蛊惑用线段树

但是聚聚没有。

发现每次单调栈都只是把后面的删掉,或是加入。

我们直接在单调栈里维护前缀最大值直接转移就好了。

××我也不知道大聚聚怎么想到的,我太蒟蒻了$QAQ$

#include <iostream>
#include <cstring>
#include <climits>
#include <cstdio>
#include <stack>
#define N 222222
#define LL long long

using namespace std;

struct Mystack{
	LL A[2*N];
	int tp;
	Mystack(){tp=0;}
	void pop(){tp--;}
	void push(const LL k){A[tp++]=k;}
	void clear(){tp=0;}
	int size(){return tp;}
	LL top(){return A[tp-1];}
	bool empty(){return tp==0;}
}st,ddp,premax;
int pn;
LL arr[N];
LL dp[N];
LL a,b,c,d;
inline LL f(LL x){
	return x*x*x*a+x*x*b+x*c+d;
}
int main(){
#ifndef LOCAL
	freopen("min.in" ,"r",stdin);
	freopen("min.out","w",stdout);
#endif
	ios_base::sync_with_stdio(false);
	cin>>pn>>a>>b>>c>>d;
	for(int i=1;i<=pn;i++)
		cin>>arr[i];
	dp[0]=0,arr[0]=-1e16;
	st.push(0);
	ddp.push(arr[0]);
	premax.push(arr[0]);
	for(int i=1;i<=pn;i++){
//		cout<<i<<" "<<dp[i-1]<<endl;
		LL pdp=dp[i-1];
		while(!st.empty() && arr[st.top()]>=arr[i]){
			pdp=max(pdp,ddp.top());
			st.pop();
			ddp.pop();
			premax.pop();
		}
		dp[i]=max(1ll*pdp+f(arr[i]),premax.top());
		st.push(i);
		ddp.push(pdp);
		premax.push(max(premax.top(),pdp+f(arr[i])));
	}
	cout<<dp[pn]<<endl;
}

T3不会

posted @ 2019-11-07 14:53  Miemeng_麦蒙  阅读(157)  评论(0编辑  收藏  举报

小麦在雨中,蒙蒙的雾气

麦蒙不想有人骚扰他,如果有必要 联系 QQ:1755601414

如果你嫌广告大,那就喷我吧,不是博客园的锅。