选举 [数据结构优化dp]

博客围绕选举问题展开,设F[i]表示前i个选民的最优答案,给出复杂度为O(N2)的解法。还介绍了优化部分,以sumn为下标建立权值线段树,每个叶子节点保存特定条件下的最大F[]值,可O(logn)实现单次转移,同时会在线段树中删除对应dp值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

选 举 选举


D e s c r i p t i o n \mathcal{Description} Description

在这里插入图片描述


S o l u t i o n \mathcal{Solution} Solution

设 F [ i ] 表 示 前 i 个 选 民 的 最 优 答 案 , 得 到 F [ i ] = m a x { F [ j ] + 1           ( s u m i &gt; s u m j ) F [ j ]                  ( s u m i = s u m j ) F [ j ] − 1           ( s u m i &lt; s u m j ) 设F[i]表示前i个选民的最优答案,得到\\ F[i]=max\begin{cases} F[j]+1\ \ \ \ \ \ \ \ \ (sum_i&gt;sum_j) \\ F[j] \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (sum_i=sum_j)\\ F[j]-1\ \ \ \ \ \ \ \ \ (sum_i&lt;sum_j) \end{cases} F[i]i,F[i]=maxF[j]+1         (sumi>sumj)F[j]                (sumi=sumj)F[j]1         (sumi<sumj)
复杂度 O ( N 2 ) O(N^2) O(N2).

优 化 部 分 : 优化部分: :
s u m n {sum_n} sumn 为下标建立 权值线段树,
线段树的每个叶子节点保存 满 足 s u m i = 叶 子 下 标 \color{blue}{满足sum_i=叶子下标} sumi= 的每个 i i i 位置上的 最 大 F [ ] 值 \color{red}{最大F[]值} F[].

则可以直接查询比当前 s u m i sum_i sumi小的,大的,相等的 最大的 F [ j ] F[j] F[j], O ( l o g n ) O(logn) O(logn) 实现单次转移.

每次 i + + i++ i++ , i − L + 1 i-L+1 iL+1 可以被合法转移, i − R + 1 i-R+1 iR+1 不能被合法转移, 于是在线段树中删除掉其对应的 d p dp dp 值.


bug
C o d e \mathcal{Code} Code

#include<bits/stdc++.h>
#define reg register
typedef std::pair<int, int> pr;

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 1e6 + 6;
const int inf = 0x3f3f3f3f;

int N;
int L;
int R;
int Lim;
int A[maxn];
int F[maxn];
int sum[maxn];

struct Node{
        std::priority_queue <pr> que;
        int l, r, max_v;
} T[maxn*4];

void Build(int k, int l, int r){ //
        T[k].l = l, T[k].r = r;
        if(l == r){ T[k].max_v = -inf; return ; }
        int mid = l+r >> 1;
        Build(k<<1, l, mid), Build(k<<1|1, mid+1, r);
        T[k].max_v = -inf; 
}

void Modify(int k, int id){ //
        int l = T[k].l, r = T[k].r;
        if(l == r){ 
                T[k].que.push(pr(F[id], id)); 
                T[k].max_v = T[k].que.top().first;
                return ;
        }
        int mid = l+r >> 1;
        if(sum[id] <= mid) Modify(k<<1, id);
        else Modify(k<<1|1, id);
        T[k].max_v = std::max(T[k<<1].max_v, T[k<<1|1].max_v);
}

void Del(int k, int id){ //
        int l = T[k].l, r = T[k].r;
        if(l == r){
                while(!T[k].que.empty() && T[k].que.top().second < Lim) T[k].que.pop();
                if(T[k].que.empty()) T[k].max_v = -inf;
                else T[k].max_v = T[k].que.top().first;
                return ;
        }
        int mid = l+r >> 1;
        if(sum[id] <= mid) Del(k<<1, id);
        else Del(k<<1|1, id);
        T[k].max_v = std::max(T[k<<1].max_v, T[k<<1|1].max_v);
}

int Query(int k, int Ql, int Qr){ //
        int l = T[k].l, r = T[k].r;
        if(Ql <= l && r <= Qr) return T[k].max_v;
        int mid = l+r >> 1;
        if(mid+1 <= Ql) return Query(k<<1|1, Ql, Qr);
        else if(mid >= Qr) return Query(k<<1, Ql, Qr); 
        return std::max(Query(k<<1, Ql, Qr), Query(k<<1|1, Ql, Qr));
}

int main(){
        freopen("election.in", "r", stdin);
        freopen("election.out", "w", stdout);
        N = read(), L = read(), R = read();
        for(reg int i = 1; i <= N; i ++) A[i] = read(), sum[i] = sum[i-1] + A[i];
        for(reg int i = 0; i <= N; i ++) sum[i] += N;
        memset(F, -0x3f, sizeof F); F[0] = 0;
        Build(1, 0, N<<1); Modify(1, 0);
        for(reg int i = L; i <= N; i ++){
                Lim = std::max(i-R, 0); 
                F[i] = std::max(F[i], Query(1, 0, std::max(0, sum[i]-1)) + 1);
                F[i] = std::max(F[i], Query(1, sum[i], sum[i]));
                F[i] = std::max(F[i], Query(1, std::min(sum[i]+1, N<<1), N<<1) - 1); 
                if(i-R >= 1) Del(1, i-R);
                if(i-L >= 0) Modify(1, i-L);
        }
        printf("%d\n", F[N]);
        return 0;
}

/*
Bug List:
1. Build -> push_up
2. For(int i = 1; i <= N; i ++)  <<-- For(int i = L; i <= N; i ++)
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值