学了一个挺帅的 MerMaid
所以用一下
考虑每次复制以后一定会粘贴若干次(大于零,否则没有意义),因此将复制粘贴捆绑起来考虑,设复制后连续粘贴了 \(m\) 次,则代价为 \(x+my\),贡献为让编辑器内文字个数变为 \(m+1\) 倍
假设我们做了 \(k\) 次上述操作,第 \(i\) 次操作的粘贴次数为 \(m_i\),则
- 总贡献为 \(\prod\limits^k_i(m_i+1)\)
- 总代价为 \(\sum\limits^k_i(x+m_iy)\)
根据题意,我们需要 \(\prod\limits^k_i(m_i+1)\gt n\),在此基础上总代价最小
转化一下总代价的式子
当 \(k\) 不变时,\(kx\) 项不变,等价于求 \(\sum^k_im_i\) 的最小值
引理:最优答案中不存在 \(i,j\),使得 \(m_i-m_j\gt 1\)
证明:设 \(m_i-m_j\gt 1\),因为 \((m_i-1)(m_j+1)=m_im_j+m_i-m_j-1=m_i(m_j+1)\gt m_im_j\),故其一定不是最优解
因此设最终答案中最小的数为 \(t\),则最大的可能值一定为 \(t+1\),如果我们钦定有 \(p\) 个 \(t\),则题目中条件等价于 \(t^p\times (t+1)^{k-p}\gt n\),可以枚举所有可能的 \(p\),通过二分答案找到最小的 \(t\) 的可能值来计算贡献
在外层枚举 \(k\) 即可,\(k\le log_2n\),因此复杂度为枚举 \(k\) 套枚举 \(p\) 套二分答案套快速幂,\(\log^4n\)
记得开 int128
#include<bits/stdc++.h>
#define int __int128
using namespace std;
long long n,x,y;
int power(int a,int t){int base=a,ans=1;while(t){if(t&1){ans=ans*base;}base=base*base;t>>=1;}return ans;
}
signed main(){int res=0x7fffffffffffffff;cin>>n>>x>>y;for(int k=1;k<=n and clock()<=0.85*CLOCKS_PER_SEC;++k){for(int i=0;i<=k;++i){int l=2,r=ceil(pow(n,(double)1/k)*2),ans=-1;while(l<=r){int mid=(l+r)/2;if(power(mid,i)*power(mid+1,k-i)>n){ans=mid;r=mid-1;}else l=mid+1;}if(ans==-1) continue;res=min(res,(__int128)k*x+((__int128)(ans-1)*i+(__int128)ans*(k-i))*(__int128)y);}}cout<<(long long)res;
}
暴力思路:可以处理出值域上每个值在原数列上的最大不出现长度 \(l_i\)(形式化地,\(l_i\) 为满足存在 \(j\),使得 \(\forall k\in[j,j+l_i-1],a_k\neq i\) 的最大值),然后对这个最大长度做前缀 \(\max\),这样就可以二分答案了,修改 \(n\) 查询 \(\log n\)
优化思路:对值域建线段树,每个点 \(i\) 记录 \(l_i\),这样查询前缀 \(\max\) 相当于查区间最值,不用重构 \(\max\) 数组,然而并没有什么优化,修改 \(n\) 查询 \(\log^2n\)
再次优化思路:由于单点修改只影响相邻的两个区间的长度,因此考虑只改变这两个区间的值
可以想到对每个值维护一个 set,记录每个值的位置,方便查询其前驱,后继,然后算出距离后进行删除,那么我们就还需要一种数据结构来支持快速删除,插入,查询最大值
赛时卡这了,没想到解法竟然是无脑上两颗优先队列
对值域上每个值开两个优先队列,一个表示现有的值,一个表示删除的值
- 加入操作:直接放进现有值的优先队列
- 删除操作:直接放进删除的值的优先队列
- 查询最大值操作:由于删除队列内的值一定是加入操作内的值的子集,因此如果当前现有值队列的对首已经被删除,则它也一定是删除值队列的对首,因此只需要判断两个队列的对首是否相等即可
修改 \(\log n\) 查询 \(\log^2n\)
#include<bits/stdc++.h>
using namespace std;
int n,q;
int a[500001];
int lstpos[500001];
namespace stree{struct tree{int maxn;}t[500001*4];#define tol (id*2)#define tor (id*2+1)#define mid(l,r) mid=((l)+(r))/2void change(int id,int l,int r,int pos,int val){if(l==r){t[id].maxn=val;return;}int mid(l,r);if(pos<=mid) change(tol,l,mid,pos,val);else change(tor,mid+1,r,pos,val);t[id].maxn=max(t[tol].maxn,t[tor].maxn);}int ask(int id,int l,int r,int L,int R){if(L<=l and r<=R){return t[id].maxn;}int mid(l,r);if(R<=mid) return ask(tol,l,mid,L,R);else if(L>=mid+1) return ask(tor,mid+1,r,L,R);return max(ask(tol,l,mid,L,mid),ask(tor,mid+1,r,mid+1,R));}
}
set<int>pos[500001];
struct getmax_t{priority_queue<int> add_t,del_t;inline void add(int x){add_t.push(x);}inline void del(int x){del_t.push(x);}inline int max(){while(del_t.empty()==false and del_t.top()==add_t.top()){del_t.pop();add_t.pop();}return add_t.top();}
};
getmax_t maxn[500001];
inline void add(int _pos){pos[a[_pos]].insert(_pos);auto iter1=pos[a[_pos]].lower_bound(_pos);iter1--;auto iter2=pos[a[_pos]].upper_bound(_pos);maxn[a[_pos]].add(_pos-*iter1-1);maxn[a[_pos]].add(*iter2-_pos-1);stree::change(1,1,n+1,a[_pos]+1,maxn[a[_pos]].max());
}
inline void del(int _pos){auto iter1=pos[a[_pos]].lower_bound(_pos);iter1--;auto iter2=pos[a[_pos]].upper_bound(_pos);maxn[a[_pos]].del(_pos-*iter1-1);maxn[a[_pos]].del(*iter2-_pos-1);pos[a[_pos]].erase(_pos);
}
int main(){ios::sync_with_stdio(false);cin>>n>>q;for(int i=1;i<=n;++i){cin>>a[i];maxn[a[i]].add(i-lstpos[a[i]]-1);pos[a[i]].insert(i);lstpos[a[i]]=i;}for(int i=0;i<=n;++i){pos[i].insert(0);pos[i].insert(n+1);maxn[i].add(n-lstpos[i]);stree::change(1,1,n+1,i+1,maxn[i].max());}while(q--){int opt,x;cin>>opt>>x;if(opt==1){del(x);del(x+1);swap(a[x],a[x+1]);add(x);add(x+1);}else{int l=0,r=n,ans=n+1;while(l<=r){int mid=(l+r)/2;if(stree::ask(1,1,n+1,1,mid+1)>=x){ans=mid;r=mid-1;}else l=mid+1;}cout<<ans<<'\n';}}
}
这道题用一种比较好玩的方式考了并行排序
部分分解法是常数较大的奇偶排序(多线程冒泡(?))
#include<bits/stdc++.h>
using namespace std;
int n;
int main(){cin>>n;cout<<((n+1)/2)*2<<endl;for(int i=1;i<=((n+1)/2);i++){for(int j=1;j+1<=n;j+=2){cout<<"CMPSWP R"<<j<<" R"<<j+1<<' ';}cout<<'\n';for(int j=2;j+1<=n;j+=2){cout<<"CMPSWP R"<<j<<" R"<<j+1<<' ';}cout<<'\n';}
}
正解用的是比较优秀的双调排序
关于双调排序的学习推荐 这篇文章