CF1495F Squares

一、题目

点此看题

二、解法

考虑每次增删格子会有什么影响,快速求 \((x,y)\) 之间的最短路就可以解决修改了。

我们把第二种边看成区间 \([l,r]\),也就是 \(r\)\(l\) 后面第一个满足 \(p_r>p_l\) 的点,现在考虑用区间去替换原来 \(i\)\(i+1\) 的边,可以把一个区间的权值定义成 \(b[l]-(a[r-1]-a[l-1])\),问题是选出不交的区间使得权值最小。

如果没有什么性质是做不动的,结论是:这些区间不会有交。考虑反证法,设四个端点 \(x_1<x_2<x_3<x_4\),如果 \([x_1,x_3]\)\([x_2,x_4]\) 分别构成区间,那么 \(p_1>p_2,p_1<p_3,p_2>p_3\),矛盾。

知道这个性质以后可以考虑离线,我们扫描左端点,维护所有右端点的答案,如果 \([l,r]\) 比里面记录区间的最优方案的总权值还要小,那么直接替换,因为区间不交所以 \([r,n+1]\) 的最短路都会受到这次替换的影响,所以是一个后缀修改,用树状数组实现即可,时间复杂度 \(O(n\log n)\)

三、总结

如果有两种决策(或多种)的问题可以考虑主一副二的方法,先固定一个然后用另一个来调整最优解。

当发现是一个做不动的问题时,可以主动去寻找结论,比如本题我们寻找关于区间的性质。

#include <cstdio>
#include <vector>
#include <iostream>
#include <set>
using namespace std;
const int M = 200005;
const int inf = 1e9;
#define pii pair<int,int>
#define make make_pair
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,q,p[M],a[M],b[M],to[M],st[M],id[M];
set<int> s;vector<pii> v[M];int ans[M],bit[M];
int lowbit(int x)
{
    return x&(-x);
}
int ask(int x)
{
    int r=0;
    for(int i=x;i>=1;i-=lowbit(i))
        r+=bit[i];
    return r;
}
void add(int x,int y)
{
    for(int i=x;i<=n+1;i+=lowbit(i))
        bit[i]+=y;
}
signed main()
{
    n=read();q=read();
    for(int i=1;i<=n;i++)
        p[i]=read();
    for(int i=1;i<=n;i++)
        a[i]=read()+a[i-1];//prefix sum
    for(int i=1;i<=n;i++)
        b[i]=read();
    st[++m]=inf;id[m]=n+1;
    for(int i=n;i>=1;i--)//maintain a stack
    {
        int l=1,r=m;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(st[mid]>p[i])
            {
                to[i]=id[mid];
                l=mid+1;
            }
            else r=mid-1;
        }
        while(p[i]>=st[m]) m--;
        st[++m]=p[i],id[m]=i;
        b[i]-=a[to[i]-1]-a[i-1];//rewrite the value
    }
    s.insert(1);s.insert(n+1);
    v[1].push_back(make(n+1,0));
    for(int i=1;i<=q;i++)//off line
    {
        int x=read();
        if(x==1) continue;
        if(s.find(x)==s.end())
        {
            auto it=s.lower_bound(x);
            int r=*it;it--;int l=*it;
            v[l].push_back(make(r,-i));
            v[l].push_back(make(x,i));
            v[x].push_back(make(r,i));
            s.insert(x);
        }
        else
        {
            s.erase(x);
            auto it=s.lower_bound(x);
            int r=*it;it--;int l=*it;
            v[l].push_back(make(r,i));
            v[l].push_back(make(x,-i));
            v[x].push_back(make(r,-i));
        }
    }
    for(int i=n;i>=1;i--)
    {
        int x=ask(to[i]);
        if(x>b[i]) add(to[i],b[i]-x);
        for(auto y:v[i])
        {
            if(y.second>=0)
                ans[y.second]+=ask(y.first)+a[y.first-1]-a[i-1];
            else
                ans[-y.second]-=ask(y.first)+a[y.first-1]-a[i-1];
        }
    }
    for(int i=1;i<=q;i++)
    {
        ans[i]+=ans[i-1];
        printf("%lld\n",ans[i]);
    }
}

posted @ 2021-07-04 11:36  C202044zxy  阅读(69)  评论(0编辑  收藏  举报