题解:P14567 【MX-S12-T2】区间

· · 题解

赛时想到的神秘做法。

我们先记下每种颜色第一次出现的位置,然后给每个其它位置赋一个随机大权值,这样赋完后再将每种颜色第一次出现的位置的权值赋为该颜色其余权值和的相反数。

显然这样可以保证同种颜色的所有权值之和为 0。我们对权值序列做前缀和,那么任意两个前缀和相同的位置中间的区间一定是合法的。

由于题目保证 f_i 单调不减,选择一个完全包含了其它合法区间的大区间更新答案一定不优,同理,若有多个位置的前缀和相同,只需要在每两个相邻位置间更新一次答案。

使用 map 记录每种前缀和上次出现的位置即可做到 O(n\log n)

:::info[代码]

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
const int N=1e6+5;
const int p=1e12;
int n,c[N],v[N],f[N];
int a[N],fi[N],sum[N],ssum[N];
mt19937 rnd(114514);
map<int,int>mp;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>c[i];
    for(int i=1;i<=n;i++)cin>>v[i];
    for(int i=1;i<=n;i++)cin>>f[i];
    for(int i=1;i<=n;i++)
        if(fi[c[i]]==0)
            fi[c[i]]=i;
    for(int i=n;i>=1;i--)
    {
        if(i!=fi[c[i]])a[i]=rnd()%p,sum[c[i]]+=a[i];
        else a[i]=-sum[c[i]];
    }
    for(int i=1;i<=n;i++)ssum[i]=ssum[i-1]+a[i];
    mp[0]=0;
    int lt=0,ans=1e18;
    for(int i=1;i<=n;i++)
    {
        if(mp.count(ssum[i]))
        {
            int l=mp[ssum[i]]+1,r=i;
            if(l>lt)
            {
                int now=0;
                for(int j=l;j<=r;j++)
                    now+=v[j]*f[j-l+1];
                ans=min(ans,now);
                lt=l;
            }
        }
        mp[ssum[i]]=i;
    }
    cout<<ans;
    return 0;
}

:::