题解:P17505 [ICPC 2026 Wuhan I] Iroha and the Kingdom of Construction
ToastBread · · 题解
好题啊!!!
下面介绍 zt17 学长放到模拟赛 sol 里的的(非卡常)做法。
记
首先思考暴力怎么做。
:::info[策略 1 使用
考虑将
显然构造完成后
:::
但是这个策略太浪费了,可以尝试去优化它。
具体地,我们有两种
:::info[策略 2.1 期望使用
注意到部分
考虑按照
划分的过程就相当于是做导弹拦截。
:::
:::info[策略 2.2 使用
考虑分块,我们按照
对于第
构造完成后,我们先把第
此时共需要
:::
可惜
但是聪明的你发现,「策略 2.1」是对「策略 1」的直接优化,而你只需要把「策略 2.2」中的提到的「策略 1」修改为「策略 2.1」就可以进一步压缩空间!
:::success[策略 3 期望使用
考虑在「策略 2.2」的基础上进行改进。
仍然沿用「策略 2.2」的分块思想,但是将「策略 2.2」中每个块的构造过程变为「策略 2.1」。
此时期望使用
:::
此时我们大约只会使用
/*
blksz:块的大小
blknum:块的数量
在实现中,我们使用 1~blknum 号栈作为 A 类栈,blknum+1 号栈以后的栈为 B 类栈
*/
#include<bits/stdc++.h>
using namespace std;
#define homo_number 114514
// #define mod 998244353
//#define mod 1000000007
#define int long long
int n,sd,a[1000005],b[1000005];
set<pair<int,int>> s;
vector<int> v;
struct pp
{
int l,r;
};
pp p[500005];
bool cmp1(pp x,pp y)
{
return x.r>y.r;
}
bool cmp2(pp x,pp y)
{
return x.l<y.l;
}
int blksz,blknum,belong[500005];
int tag[5005],sum[5005],st[5005],ed[5005],sz[5005];
void build()//2.2 分块
{
blksz=pow(n,2.0/3.0);
for(int i = 1;; i++)
{
st[i]=(i-1)*blksz+1;
ed[i]=i*blksz;
if(ed[i]>=n)
{
ed[i]=n;
blknum=i;
break;
}
}
for(int i = 1; i <= blknum; i++)
{
for(int j = st[i]; j <= ed[i]; j++)
{
belong[j]=i;
}
sz[i]=ed[i]-st[i]+1;
}
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i = 1; i <= 2*n; i++) cin>>a[i];
for(int i = 1; i <= 2*n; i++)
{
if(p[a[i]].l==0) p[a[i]].l=i;
else p[a[i]].r=i;
}
sort(p+1,p+n+1,cmp1);
build();//构造 2.2 的分块
for(int i = 1; i <= blknum; i++)
{
//对于 2.2 的每一个块,尝试执行 2.1 的策略
for(int j = st[i]; j <= ed[i]; j++)
{
b[p[j].l]=i;
}
sort(p+st[i]+1,p+ed[i]+1,cmp2);
s.clear();
int nowcnt=blknum;
for(int j = st[i]; j <= ed[i]; j++)
{
auto it=s.lower_bound(make_pair(p[j].r,0));
if(it==s.begin())
{
nowcnt++;
b[p[j].r]=nowcnt;
s.insert(make_pair(p[j].r,nowcnt));
}
else
{
it--;
b[p[j].r]=(*it).second;
s.erase(it);
s.insert(make_pair(p[j].r,b[p[j].r]));
}
}
}
for(int i = 1; i <= 2*n; i++) cout<<b[i]<<' ';
return 0;
}
[^1]: 严格来说,随机排列下 LDS 的期望为