CF2064E Mycraft Sand Sort 题解
第一次一眼秒了一道 E,但是被人均六分钟 C 题硬控一小时,未能写完,遗憾离场,特此纪念。
考虑第一列,无论排列 是什么样子,第一列一定是 。因此, 数组一定等于 数组。
这个结论还引出一个推论, 一定由 中颜色相同的位置交换得到。如果异色交换,一定会有一种颜色的方块总数变化,此时一定不与初始状态相同。
那我们先考虑一种颜色。一种颜色某个元素可以邻项交换的条件是这两个元素之间的位置大小必须都小于这个位置的大小。手玩一下充分性是显然的,考虑之间的位置大小大于的情况一定会导致某两个方块颜色改变,所以也是必要的。
那么,一个元素可以交换的位置是一段连续的区间,即左右都到第一个大于这个元素的异色位置。这种计数问题的经典做法是先考虑最紧的限制。我们先考虑最小的元素,从小到大依次安排位置。当我们安排一个更大的元素时,这个元素的区间要么和之前的区间无交,要么完全包含。我们把占位置的贡献挂到每段可行区间的左端点,统计有多少个位置已经被占只需要查询可行区间的区间和即可。
然后我们发现每种颜色是独立的,我们先交换小的元素,这样就不会被后来交换的元素影响,一定存在方案。直接乘起来就行了。
上面提到的东西都可以使用线段树维护,使用普通二分时间复杂度 ,使用线段树二分时间复杂度 。
#include <bits/stdc++.h>
#define lc(x) ((x)<<1)
#define rc(x) ((x)<<1)|1
using namespace std;
struct node
{
long long mx,sum;
}tr1[1200000],tr2[1200000];
long long t,n,p[300000],c[300000],rt=1;
vector<long long>a[300000];
pair<long long,long long>b[300000];
const long long mod=998244353;
void pushup(struct node tr[],long long x)
{
tr[x].mx=max(tr[lc(x)].mx,tr[rc(x)].mx);
tr[x].sum=tr[lc(x)].sum+tr[rc(x)].sum;
}
void build1(long long x,long long l,long long r)
{
tr1[x].mx=tr1[x].sum=0;
if(l==r)
{
tr1[x].mx=p[l];
return;
}
long long mid=(l+r)>>1;
build1(lc(x),l,mid),build1(rc(x),mid+1,r);
pushup(tr1,x);
}
long long query1(long long x,long long l,long long r,long long lx,long long rx)
{
if(lx>rx)return 0;
if(l>=lx&&r<=rx)return tr1[x].mx;
long long mid=(l+r)>>1,ans=0;
if(lx<=mid)ans=max(ans,query1(lc(x),l,mid,lx,rx));
if(rx>=mid+1)ans=max(ans,query1(rc(x),mid+1,r,lx,rx));
return ans;
}
void build2(long long x,long long l,long long r,long long c)
{
tr2[x].mx=tr2[x].sum=0;
if(l==r)
{
tr2[x].mx=query1(rt,1,n,a[c][l-1]+1,a[c][l]-1);
return;
}
long long mid=(l+r)>>1;
build2(lc(x),l,mid,c),build2(rc(x),mid+1,r,c);
pushup(tr2,x);
}
void add(long long x,long long l,long long r,long long p,long long k)
{
if(l==r)
{
tr2[x].sum+=k;
return;
}
long long mid=(l+r)>>1;
if(p<=mid)add(lc(x),l,mid,p,k);
else add(rc(x),mid+1,r,p,k);
pushup(tr2,x);
}
long long query2(long long x,long long l,long long r,long long lx,long long rx)
{
if(lx>rx)return 0;
if(l>=lx&&r<=rx)return tr2[x].mx;
long long mid=(l+r)>>1,ans=0;
if(lx<=mid)ans=max(ans,query2(lc(x),l,mid,lx,rx));
if(rx>=mid+1)ans=max(ans,query2(rc(x),mid+1,r,lx,rx));
return ans;
}
long long query3(long long x,long long l,long long r,long long lx,long long rx)
{
if(lx>rx)return 0;
if(l>=lx&&r<=rx)return tr2[x].sum;
long long mid=(l+r)>>1,ans=0;
if(lx<=mid)ans=ans+query3(lc(x),l,mid,lx,rx);
if(rx>=mid+1)ans=ans+query3(rc(x),mid+1,r,lx,rx);
return ans;
}
int main()
{
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)a[i].clear(),a[i].push_back(0);
for(int i=1;i<=n;i++)scanf("%lld",&p[i]);
for(int i=1;i<=n;i++)scanf("%lld",&c[i]),a[c[i]].push_back(i);
build1(rt,1,n);
long long ans=1;
for(int i=1;i<=n;i++)
if(a[i].size()>1)
{
long long w=a[i].size()-1;
build2(rt,1,w,i);
for(int j=1;j<=w;j++)b[j].first=p[a[i][j]],b[j].second=j;
sort(b+1,b+w+1);
for(int j=1;j<=w;j++)
{
long long l=1,r=b[j].second-1,lx=r+1,rx=r+1;
while(l<=r)
{
long long mid=(l+r)>>1;
if(query2(rt,1,w,mid+1,b[j].second)<b[j].first)lx=mid,r=mid-1;
else l=mid+1;
}
l=b[j].second+1,r=w;
while(l<=r)
{
long long mid=(l+r)>>1;
if(query2(rt,1,w,b[j].second+1,mid)<b[j].first)rx=mid,l=mid+1;
else r=mid-1;
}
ans=ans*(rx-lx+1-query3(rt,1,w,lx,rx))%mod,add(rt,1,w,lx,1);
}
}
printf("%lld\n",ans%mod);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探