很多时候记录贡献比动态维护能够得到更优的复杂度
Codeforces Round 809 (Div. 2) D2
很容易观察到每个位置有 O ( n ) O(\sqrt n) O(n)种取值,我们找出这些值,从大到小枚举最小值的同时维护最小的最大值即可,得到了一个 O ( n n l o g n ) O(n\sqrt nlogn) O(nnlogn)的解法(常数也很大),但是不足以通过本题。
int n,m,k;
int a[N];
vector<int>nxt[N];
void solve(){
cin>>n>>k;
for(int i=1;i<=n;i++)nxt[i].clear();
set<int>val;
for(int i=1;i<=n;i++){
cin>>a[i];
for(int l=1;l<=k;){
if(l>a[i]){
val.insert(0);
nxt[i].pb(a[i]/l);
break;
}
int r=a[i]/(a[i]/l);
val.insert(-(a[i]/l));
nxt[i].pb(a[i]/l);
l=r+1;
}
reverse(nxt[i].begin(),nxt[i].end());
}
priority_queue<PII>q;
int ans=inf,mn=inf;
for(int i=1;i<=n;i++)q.push(mp(a[i],i)),mn=min(mn,a[i]);
for(int tx:val){
int x=-tx;
if(x>mn)continue;
int pos=q.top().se;
while(nxt[pos].size()>1&&nxt[pos][nxt[pos].size()-2]>=x){
PII t=q.top();q.pop();
nxt[pos].pop_back();
q.push(mp(nxt[pos].back(),t.se));
pos=q.top().se;
}
ans=min(ans,q.top().fi-x);
if(nxt[pos].size()<=1)break;
}
cout<<ans<<'\n';
}
我们利用一个数组 m n _ m x mn\_mx mn_mx来记录贡献, m n _ m x [ i ] mn\_mx[i] mn_mx[i]表示当最小值为 i i i时,最大值最小为多少,我们在计算每个位置可能的取值时把它处理出来即可,得到了 O ( n n ) O(n\sqrt n) O(nn)的解法,可以通过本题。
int n,m,k;
int a[N],mn_mx[N],num[N];
void solve(){
cin>>n>>k;
unordered_set<int>del;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){
int pre=inf;
for(int l=1;l<=k&&l<=a[i];){
int r=a[i]/(a[i]/l);
int val=a[i]/l;
num[val]=1;
mn_mx[val+1]=max(mn_mx[val+1],pre);
del.insert(val),del.insert(val+1);
pre=val;
l=r+1;
}
mn_mx[0]=max(mn_mx[0],pre);
}
int ans=inf,up=mn_mx[0];
for(int i=0;i<=a[1];i++){
up=max(up,mn_mx[i]);
if(num[i])ans=min(ans,up-i);
}
for(int x:del)num[x]=mn_mx[x]=0;
num[0]=mn_mx[0]=0;
cout<<ans<<'\n';
}
Educational Codeforces Round 125 (Rated for Div. 2) D
可以观察到对于第 i i i个 m o n s t e r monster monster,我们要求的就是 m i n 1 ≤ j ≤ n ( H i D i h j d j + 1 ) ∗ c j \mathop{min} \limits_{1\leq j\leq n} {(\frac{H_iD_i}{h_jd_j}+1)*c_j} 1≤j≤nmin(hjdjHiDi+1)∗cj,观察到 H i D i h j d j \frac{H_iD_i}{h_jd_j} hjdjHiDi最多有 O ( H i D i ) O(\sqrt{H_iD_i}) O(HiDi)种取值,我们将不同的 u n i t unit unit按 h i d i h_id_i hidi从小到大排序,再用 S T ST ST表处理出区间最小值,可以得到一个 O ( m H D / C l o g n + n l o g n ) O(m\sqrt{HD/C}logn+nlogn) O(mHD/Clogn+nlogn)的方法,同样不足以通过此题。
int n,m,k;
int a[N],b[N],tot=0;
int C;
int ST[N][21],lg2[N];
void init_ST(int* num,int n){
for(int i=1;i<=n;i++)ST[i][0]=num[i];
for(int j=1;j<=20;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
ST[i][j]=min(ST[i][j-1],ST[i+(1<<(j-1))][j-1]);
}
}
}
int query(int l,int r){
int mid=lg2[r-l+1];
return min(ST[l][mid],ST[r-(1<<mid)+1][mid]);
}
int find(int s,int x){
int l=s,r=tot;
if(b[r]<=x)return r;
while(l+1<r){
int mid=l+r>>1;
if(b[mid]<=x)l=mid;
else r=mid;
}
return l;
}
inline void solve(){
cin>>n>>C;
lg2[0]=-1;
for(int i=1;i<=n;i++)lg2[i]=lg2[i>>1]+1;
map<int,int>num;
for(int i=0;i<n;i++){
int c,d,h;cin>>c>>d>>h;
if(num.count(d*h))num[d*h]=min(num[d*h],c);
else num[d*h]=c;
}
tot=0;
for(auto t:num){
a[++tot]=t.se;
b[tot]=t.fi;
}
init_ST(a,tot);
cin>>m;
while(m--){
int D,H;cin>>D>>H;
int ans=INF,mul=D*H;
for(int i=find(1,mul/C);i<=tot;i++){
int t=mul/b[i],vr;
if(t)vr=mul/t;
else vr=INF;
int pr=find(i,vr);
int mn=query(i,pr);
ans=min(ans,(t+1)*mn);
i=pr;
}
if(ans>C)cout<<-1<<' ';
else cout<<ans<<' ';
}
}
我们还观察到,使用 k k k个 u n i t unit unit可以击败 1 ≤ H D ≤ k ∗ h d − 1 1\leq HD\leq k*hd-1 1≤HD≤k∗hd−1内的所有怪物,于是同样的,我们利用数组 d p dp dp来计算贡献, d p [ i ] dp[i] dp[i]表示花费为 i i i时,可以打败 H D HD HD最大为多少的怪物,显然它是单调不降的,处理之后在 d p dp dp数组上二分即可,复杂度为 O ( ( n + m ) l o g C ) O((n+m)logC) O((n+m)logC)。
int n,m,k;
int dp[N];
int C;
inline void solve(){
cin>>n>>C;
map<int,int>num;
for(int i=0;i<n;i++){
int c,d,h;cin>>c>>d>>h;
if(num.count(c))num[c]=max(num[c],d*h);
else num[c]=d*h;
}
for(auto t:num){
for(int j=t.fi;j<=C;j+=t.fi){
int cnt=j/t.fi;
dp[j]=max(dp[j],cnt*num[t.fi]-1);
}
}
for(int i=1;i<=C;i++)dp[i]=max(dp[i],dp[i-1]);
cin>>m;
while(m--){
int H,D;cin>>H>>D;
int l=0,r=C;
if(dp[r]<H*D){
cout<<-1<<' ';
continue;
}
while(l+1<r){
int mid=l+r>>1;
if(dp[mid]<H*D)l=mid;
else r=mid;
}
cout<<r<<' ';
}
}