题解:P11987 [JOIST 2025] 集邮比赛 4 / Collecting Stamps 4
传送门
闲话:这道题被拿来作考试 T3,我成功证明出重要结论并打出
题意
有一个节点数为
设
思路
假设我们已经确定了起始点
有一个重要的结论:如果按顺序依次遍历
考虑证明:首先,一次交换最多改变两种颜色的位置关系(例如
设
-
如果
z 与y 的位置关系为\dots z \dots z,y \dots y \dots ,那么交换中间的z,y 即可达到目标。 -
否则位置关系为
\dots z,y \dots z \dots y \dots 或\dots z,y \dots y \dots z \dots ,已满足l_{z} < r_{y} 且l_{y} < r_{z} ,由于其它相邻位置交换颜色不会影响到其它颜色的位置关系,即z 第一次出现的位置和它左边的位置交换不会影响y,z 的位置关系,因此我们将y 改为z 继续进行分讨。
由于一定存在
此时
考虑以每个位置为起点,不妨设该点为
对于查询,先把查询离线下来并按
考虑优化统计不进行任何操作时从
欸?发现什么没有,
总复杂度
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,x,q,sum=0,a[2000005],c[1000005],l[1000005],r[1000005],minn[1000005],ans[1000005];
struct node
{
int us,cs;
}p[1000005];
struct nide
{
int c,id;
}ask[500005];
bool cmp1(node x,node y)
{
return x.cs<y.cs;
}
bool cmp2(nide x,nide y)
{
return x.c<y.c;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>x;
for(int i=1;i<=2*n;i++)
{
cin>>a[i];
}
for(int i=1;i<=2*n;i++)
{
cin>>c[i];
}
for(int i=2*n;i>=1;i--)
{
if(r[a[i]]==0)
{
r[a[i]]=i;
continue;
}
l[a[i]]=i;
sum+=l[a[i]];
}
for(int i=1;i<=2*n;i++)
{
if(i!=1)
{
sum-=l[a[i-1]];
swap(l[a[i-1]],r[a[i-1]]);
r[a[i-1]]+=2*n;
sum+=l[a[i-1]];
sum-=n;
}
p[i].cs=n*n+n*(n+1)/2-sum;
p[i].us=c[i];
}
sort(p+1,p+2*n+1,cmp1);
p[0].cs=0;
p[0].us=5e18;
minn[2*n+1]=5e18;
for(int i=2*n;i>=0;i--)
{
minn[i]=min(minn[i+1],p[i].us);
}
cin>>q;
for(int i=1;i<=q;i++)
{
cin>>ask[i].c;
ask[i].id=i;
}
sort(ask+1,ask+q+1,cmp2);
int nu=5e18,nc=0,nh=0;
for(int i=1;i<=q;i++)
{
int c=ask[i].c,id=ask[i].id;
while(nh<2*n&&p[nh+1].cs<=c)
{
nh++;
nu=nu+(p[nh].cs-nc)*x;
nu=min(nu,p[nh].us);
nc=p[nh].cs;
}
ans[id]=min(nu+(c-nc)*x,minn[nh+1]);
}
for(int i=1;i<=q;i++)
{
cout<<ans[i]<<'\n';
}
return 0;
}