又是做题笔记
说在前面
都是一些 OI Bingo 上的题,可能还会有部分 ABC 的题。
P5929 [POI1999] 地图
题目大意
将一个长度为 \(N\) 的序列染成 \(M\) 种颜色。
对于一个颜色 \(k\),要求染 \(k\) 颜色的点权与 \(A(k)\) 的差的绝对值尽量地小。
其中 \(A(k)\) 定义如下:
- 在染颜色 \(k\) 的点中至少有一半的点的权值不大于 \(A(k)\)。
- 在染颜色 \(k\) 的点中至少有一半的点的权值不小于 \(A(k)\)。
求最小误差。
解题思路
首先看到这个 \(A(k)\),这个是什么?
考虑直接将 \(a\) 升序排序,令染颜色 \(k\) 的最小的点下标为 \(l\),最大的点下标为 \(r\)。
那么 \(a_{\frac {l+r}{2}}\) 就是 \(A(k)\)。
考虑 dp,设 \(f_{i,j}\) 表示前 \(i\) 个数中用 \(j\) 种颜色染色的最小误差。
记 \(l\) 与 \(r\) 之间的误差为 \(calc(l,r)\),就可以得到状态转移:
\(f_{i,j} = \min _{k=1}^{i} f_{k-1,j-1} + calc(k,i)\)。
如何在 \(O(1)\) 内求出 \(calc(l,r)\)?
令 \(md = \frac {l+r}{2}\)。
\(calc(l,r) = \sum _{i=md+1}^{r} a_i-a_{md} + \sum _{i=l}^{md-1} a_{md}-a_i\)。
将 \(a_{md}\) 全部提取出来,就可以得到:
\(calc(l,r) = \sum _{i=md+1}^{r} a_i -(r-md) \times a_{md} +(md-l) \times a_{md} - \sum _{i=l}^{md-1} a_i\)。
注意到 \(\sum _{i=md+1}^{r} a_i\) 和 \(\sum _{i=l}^{md-1} a_i\) 都可以用前缀和解决,那么这道题就做完了,最终答案为 \(f_{n,m}\)。
程序时间复杂度 \(O(N^2 \times M)\)。
计算 \(calc\) 部分:
int calc(int l,int r){
int md=(l+r)>>1;
int sum1=sum[r]-sum[md];
int sum2=sum[md-1]-sum[l-1];
int m1=a[md]*(r-md),m2=a[md]*(md-l);
return sum1-m1+m2-sum2;
}
状态转移部分:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=i;k++){
f[i][j]=min(f[i][j],f[k-1][j-1]+calc(k,i));
}
P7972 [KSN2021] Self Permutation
题目大意
给定一个长度为 \(N\) 的排列 \(a_i\),每次操作选择两个相邻的数,删除它们中较大的那个。
问最后可能得到序列的数量,答案对 \(10^9+7\) 取模。
解题思路
通过理解题目、手模大样例,可以得出以下重要性质:
- 如果 \(a_l\) 与 \(a_r\) 能够被删除至相邻,那么 \(\min _{i=l}^{r} a_i = \min(a_l,a_r)\)
考虑 dp,设 \(f_i\) 表示前 \(i\) 个数能够构成的最后的序列的个数。
然后就有一个很显然的时间复杂度为 \(O(N^2)\) 的转移,枚举上一个位置 \(j\),但这样会超时。
考虑用单调栈,对于每个 \(i\) 求它左边和右边第一个小于 \(a_i\) 的位置,记为 \(l_i\) 和 \(r_i\)。
如果两个位置 \(i\) 和 \(j\) 可以相邻,当且仅当 \([l_i,r_i]\) 与 \([l_j,r_j]\) 相交。
就可以用树状数组优化 dp 解决,时间复杂度 \(O(N \times \log N)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
const int mod=1e9+7;
int n,a[N],f[N];
int l[N],r[N];
struct BIT{
int c[N];
int lowbit(int x){
return x&-x;
}
void update(int x,int k=1){
for(;x<=n;x+=lowbit(x))
(c[x]+=k)%=mod;
}
int query(int x){
int res=0;
for(;x;x-=lowbit(x))
(res+=c[x])%=mod;
return res;
}
}Tr;
struct my_stack{
int idx;
vector<int> vec;
int size(){
return idx;
}
bool empty(){
return idx==0;
}
void init(int x){
vec.reserve(x);
}
int top(){
return vec[idx];
}
void push(int x){
vec[++idx]=x;
return;
}
void pop(){
idx--;
}
void pop_k(int k){
idx-=k;
}
}stk;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
stk.init(n+5);
for(int i=1;i<=n+1;i++){
while(stk.size()&&a[stk.top()]>a[i]){
r[stk.top()]=i-1;
stk.pop();
}
stk.push(i);
}
for(int i=n;i>=0;i--){
while(stk.size()&&a[stk.top()]>a[i]){
l[stk.top()]=i+1;
stk.pop();
}
stk.push(i);
}
Tr.update(1);
for(int i=1;i<=n;i++){
f[i]=((Tr.query(n)-Tr.query(l[i]-1))%mod+mod)%mod;
Tr.update(r[i],f[i]);
}
int minn=1<<30,ans=0;
for(int i=n;i>=1;i--)
if(minn>=a[i]){
minn=a[i];
(ans+=f[i])%=mod;
}
cout<<ans<<"\n";
return 0;
}

浙公网安备 33010602011771号