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;
}

浙公网安备 33010602011771号