又是做题笔记

说在前面

都是一些 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;
}

posted @ 2025-01-16 14:40  SunburstFan1106  阅读(16)  评论(0)    收藏  举报