【2020NOI.AC省选模拟#7】C. 逃课

题目链接

原题解:

我们需要找出两个距离不超过$k-1$的位置,使他们的和尽量大。

被修改过得位置比较少,所以我们可以先求出连个位置都没有被修改过的答案,然后只关心那些涉及到至少一个被修改过的位置的方案。

我们可以对每个位置维护一个multiset,表示可以和当前位置同时选择的被修改过的位置的值的集合。

我们需要知道每个位置本来的值加上multiset的最大值的最大值。

由于修改某个位置是需要在这个位置左右两边的两个区间的multiset中同时插入或删除一个值,我们可以用线段树维护集合,某个区间的节点上的multiset中的元素代表这个区间中每个multiset里都有一个当前元素。

我们还需要维护区间最大值。

由于每次修改最多引起$O(\log N)$次multiset操作,复杂度为$O(n+q\log ^2 n)$。

补充:

用STL来帮助做树套树。只用存修改后的值。

 

代码(100分):

#include<cstdio>
#define M 3700001
#define N 2097152
inline int min(int u,int v){return u<v?u:v;}
inline int max(int u,int v){return u<v?v:u;}
int a[N],b[M],c[M],d[M],e[N],f[N],g[N],m,n,p,q,t;
inline void add(int u,int v,int w){b[++t]=a[u],c[a[u]=t]=v,d[t]=w;}
inline void up(int u){g[u]=max(g[u<<1],g[u<<1|1]);}
inline void modify(int u,int v){for(g[u|=m]=v;u>>=1;up(u));}
inline int query(int u,int v)
{
    int w=0;
    for(u+=m-1,v+=m+1;u^v^1;u>>=1,v>>=1)
    {
        if(~u&1)w=max(w,g[u^1]);
        if(v&1)w=max(w,g[v^1]);
    }
    return w;
}
inline void add(int u,int v,int x,int y)
{
    for(u+=m-1,v+=m+1;u^v^1;u>>=1,v>>=1)
    {
        if(~u&1)add(u^1,x,y);
        if(v&1)add(v^1,x,y);
    }
}
void dfs(int u,int v)
{
    for(int i=a[u];i;i=b[i])v=max(v,d[i]+query(max(1,c[i]-p+1),min(n,c[i]+p-1))),modify(c[i],d[i]);
    u<m?dfs(u<<1,v),dfs(u<<1|1,v),0:q<(u^m)?0:printf("%d\n",v);
    for(int i=a[u];i;i=b[i])modify(c[i],0);
}
int i,u,v,w;
int main()
{
    for(scanf("%d%d%d",&n,&p,&q),m=1;m<n+2||m<=q;m<<=1);
    for(i=1;i<=n;i++)scanf("%d",e+i);
    for(i=1;i<=q;i++)scanf("%d%d",&u,&v),add(f[u],i-1,u,e[u]),e[u]=v,f[u]=i;
    for(i=1;i<=n;i++)f[i]?add(f[i],q,i,e[i]):add(1,i,e[i]);
    return dfs(1,0),0;
}
View Code

 

posted @ 2020-06-07 22:39  汉谡  阅读(182)  评论(0编辑  收藏  举报